1

给定一个有一堆成员的类,每个成员都有自己的 getter/setter/etc 方法,有没有办法设计一个切入点,当包含在父类中时,它只会触发成员的方法?

例如:

public MyClass{
   List myList = new ArrayList<String>();
}

如果我想创建一个切入点来建议 myList.add(),有没有办法做到这一点?我不想建议所有 ArrayList.add() 调用。仅适用于属于 MyClass 成员的 Collections.add()。

我试过玩withinand 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 外部调用也是如此。

4

1 回答 1

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 - 更好的方面修改:

这个更优雅的解决方案归功于 Andy Clement(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]
于 2014-07-17T07:55:26.897 回答