В любом случае, чтобы создать pointcut для методов членов класса?
Имеется ли класс с множеством членов, каждый из которых имеет свои собственные методы getter/setter/etc, есть ли способ создания pointcut, который будет запускаться только для методов членов, если он содержится в родительском классе?
Например:
public MyClass{
List myList = new ArrayList<String>();
}
Если я хочу создать pointcut для рекомендации myList.add(), есть ли способ сделать это? Я не хочу советовать все вызовы ArrayList.add(). Только для Collections.add(), которые являются членами MyClass.
Я пытался поиграть с within
а также cflow
, но безрезультатно:
pointcut addPointcut() : cflow( execution( * *.getMyList() ) ) && call( * *.add(..));
но это не похоже на работу. Я предполагаю, что, учитывая, что add()
вызовы на самом деле не являются частью потока управления get(), похоже, он не запускается должным образом.
После еще одной игры, я заметил, что работает следующее решение:
pointcut addPointcut(): within( MyClass ) && call( * *.add(..) );
Это правильная реализация?
Я пытался ограничить pointcut только советами вызывать add () при передаче объекта @Entity, но он не работает. Пример:
pointcut addEntityPointcut(): within( MyClass ) && call( * *.add(@javax.persistence.Entity *) );
и все же addPointcut()
работает при вызове с параметром @Entity.
Тип аргумента, основанный на фактическом вызывающем методе, или основанный на add()
подпись?
РЕДАКТИРОВАТЬ
Я был слишком быстр, чтобы сделать неверный вывод. После сна я пришел к выводу, что мой pointcut не будет работать.
public class FirstClass{
List<String> strings = new ArrayList<>();
// getters and setters
}
public class Execute{
public main(){
FirstClass fc = new FirstClass();
fc.getStrings().add( "This call is advised" ); // <---- Is there any way to advise this add() method?
List<String> l = new ArrayList<>();
l.add( "This call is not advised" ); // <---- this one should not be advised
}
}
Я ищу способ посоветовать метод add (), вызываемый из любого класса. Тем не менее, я только хочу посоветовать метод add () в списке членов, содержащемся в FirstClass, даже если он вызывается извне FirstClass.
1 ответ
Тип аргумента, основанный на фактическом вызывающем методе, или основанный на
add()
подпись?
В AspectJ для call()
pointcut вам нужно указать сигнатуры метода или конструктора. add()
Метод в этом случае не имеет никаких параметров, аннотированных @Entity
Таким образом, то, что вы пытаетесь сделать, не работает. Это обходной путь с использованием отражения:
Образец аннотации:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {}
Образец объекта:
package de.scrum_master.app;
@Entity
public class MyEntity {}
Приложение для водителя:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.myList.add("bar");
application.myList.add(new MyEntity());
}
}
аспект:
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject);
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
Выход:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@19dc6592
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@54906181
Что касается варианта согласования потока управления, я думаю, что с точки зрения именования имеет смысл предположить, что getMyList()
ничего не добавляет, а просто возвращает список. Вероятно, вы скорее делаете что-то вроде application.getMyList().add("foo")
и в этом случае add()
действительно вне (после) потока управления getMyList()
потому что он действует на свой результат.
Если OTOH у вас есть гипотетический метод addToList(Object element)
который действительно зовет add()
ты можешь использовать cflow()
, Давайте изменим пример кода:
Модифицированный драйвер приложения:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public void addToMyList(Object element) { reallyAddToMyList(element); }
private void reallyAddToMyList(Object element) { myList.add(element); }
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.addToMyList("bar");
application.addToMyList(new MyEntity());
}
}
Модифицированный аспект:
package de.scrum_master.aspect;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject));
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
Новый вывод:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@323ba00
Как видите, зарегистрирован только один звонок. Это один из reallyAddToMyList()
а не тот из main()
,
Обновление 2014-07-21 - лучшая модификация аспекта:
Авторы этого более элегантного решения получают Энди Клемент (сопровождающий AspectJ), который упомянул об этом в списке рассылки AspectJ. Он показывает оба моих варианта сверху, но использует && @args(Entity)
вместо if (addedObject.getClass().isAnnotationPresent(Entity.class))
:
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject) && @args(Entity);
before(Object addedObject) : addEntity(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
pointcut addEntitySpecial(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject)) && @args(Entity);
before(Object addedObject) : addEntitySpecial(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject + " [special]");
}
}
Вывод с активными обоими вариантами выглядит так:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@229ff6d1
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e [special]