当前位置: 首页 > 知识库问答 >
问题:

是否创建指向类成员方法的切入点?

东明德
2023-03-14

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

例如:

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

如果我想创建一个切入点来建议myList。add(),是否有方法执行此操作?我不想通知所有ArrayList。add()调用。仅限于收集。是MyClass成员的add()。

我尝试过在和cflow中使用,但没有用:

pointcut addPointcut() : cflow( execution( * *.getMyList() ) ) && call( * *.add(..));

但它似乎不起作用。我假设,鉴于add()调用实际上不是get()控制流的一部分,它似乎没有正确触发。

再玩了一会儿后,我注意到以下解决方案似乎有效:

pointcut addPointcut(): within( MyClass ) && call( * *.add(..) );

这是正确的实现吗?

我试图将切入点限制为仅建议在传递@Entity对象时调用add(),但它不起作用。Ex:

pointcut addEntityPointcut(): within( MyClass ) && call( * *.add(@javax.persistence.Entity *) );

然而,addPointcut()。

参数类型是基于实际调用方法,还是基于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中包含的成员列表提供add()方法的建议,即使从FirstClass外部调用。


共有1个答案

华献
2023-03-14

参数类型是基于实际调用方法,还是基于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()不添加任何内容,只返回一个列表是有意义的。可能您更愿意做一些类似于应用程序的事情。getMyList()。add(“foo”),在本例中,add()确实在getMyList()的控制流之外(之后),因为它对其结果进行操作。

如果您有一个假想的方法addToList(objectelement),它真正调用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-更好的方面修改:

这个更优雅的解决方案的功劳归于安迪·克莱门特(AWS J维护者),他在AWS J邮件列表中提到了这个解决方案。它从上面显示了我的两个变体,但是使用了

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]

 类似资料:
  • 所以我的教授喜欢用我们应该弄清楚的论文上的代码来测试我们。我不会发布代码,因为我不是在寻找答案,这只是他曾经让我们感到困惑的一件事是,当他初始化一个类的对象,然后初始化另一个类的指针指向上述对象时。它让我感到困惑,这是一个如此具体的问题,我不知道如何搜索它。例: “d- 当指针来自一个类却指向另一个类的对象时,它到底在做什么?我知道这个问题以前可能已经被回答过了,但是我在任何地方都找不到它,所以我

  • 我正在学习如何在C中使用并写了以下示例: 问:是否保证在所有情况下指向一个结构的指针都是指向它的第一个元素的完全相同的指针? 在这种特殊的情况下,它能像我预期的那样工作,但我不确定它是否能得到保证。编译器可以在开始时插入一些填充吗? 我唯一能找到的关于结构类型布局的是N1570的类型: 结构类型描述了一组按顺序分配的非空成员对象(在某些情况下,还包括一个不完整的数组),每个对象都有一个可选的指定名

  • 我试图理解在将值存储到结构或联合的成员中时,类型双关是如何工作的。 标准N1570指定 当值存储在结构或联合类型的对象(包括成员对象)中时,与任何填充字节相对应的对象表示的字节采用未指定的值。 所以我把它解释为如果我们有一个对象要存储到一个成员中,这样对象的大小等于,与填充相关的字节将具有未指定的值(即使我们定义了原始对象中的字节)。这是一个例子: 在我的机器。 打印的行为是否为这样的程序定义得很

  • 问题内容: 我有点困惑。问题是标题,这就是为什么我要问。我在单个VM上运行了JSF + JPA Web应用程序。JPA类具有字段。现在想象一下某个网络用户打开了一个页面并执行以下代码 当另一个Web用户/线程尝试读取该瞬态值时,我期望什么输出: 换句话说,就JVM而言,方法是否总是返回新的类实例或相同或“取决于”的实例?我一直在通过JSR-220寻找答案,但没有成功,任何帮助或文档参考都将不胜感激

  • 问题内容: 我正在尝试扩展库以进行集成,并通过将config设置为自动(可移植)来实现,这意味着以编程方式添加元素。(我知道可以通过Hibernate 或EclipseLInk来实现,但是- 可移植性)。我也想避免仅用于此单一目的。 我可以动态创建一个,并用指定包中的元素填充它(通过Reflections库)。当我尝试将其提供给提供程序时,问题就开始了。我能想到的唯一方法是设置一个,但我想不出什么

  • GCC 8.2.1和MSVC 19.20编译以下代码,但Clang 8.0.0和ICC 19.0.1无法这样做。 Clang 8.0.0 的错误消息如下: 我注意到它在两种情况下可以用Clang编译: 从最后一个定义中删除括性 将线 替换为