给定一个包含一堆成员的类,每个成员都有自己的getter / setter / etc方法,有没有办法设计一个只会触发成员的切入点。包含在父类中的方法?
例如:
public MyClass{
List myList = new ArrayList<String>();
}
如果我想创建一个切入点来建议myList.add(),有没有办法做到这一点?我不希望建议所有ArrayList.add()调用。仅限属于MyClass成员的Collections.add()。
我尝试过玩within
和cflow
,但无济于事:
pointcut addPointcut() : cflow( execution( * *.getMyList() ) ) && call( * *.add(..));
但它似乎不起作用。我认为假设add()
调用实际上不是get()控制流的一部分,它似乎没有正确触发。
经过多次游戏,我发现以下解决方案似乎有效:
pointcut addPointcut(): within( MyClass ) && call( * *.add(..) );
这是正确的实施吗?
我试图将切入点限制为仅在传递@Entity对象时建议调用add(),但它不起作用。例如:
pointcut addEntityPointcut(): within( MyClass ) && call( * *.add(@javax.persistence.Entity *) );
然而addPointcut()
在使用@Entity作为参数调用时有效。
参数类型是基于实际的调用方法,还是基于add()
签名?
修改
我太快就错误地得出了错误的结论。睡了之后,我才意识到我的切入点不起作用。
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()方法。但是,我只想在FirstClass中包含的成员List上建议add()方法,即使从FirstClass外部调用也是如此。
答案 0 :(得分:2)
参数类型是基于实际的调用方法,还是基于
add()
签名?
在AspectJ中call()
切入点,您需要指定方法或构造函数签名。在这种情况下,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 mailing list上提到它的Andy Clement(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]