java - 从方面函数访问被调用者类的数据成员

标签 java spring aop aspectj spring-aop

我有一个 DoStuff 类,它有 ServiceSAOServiceInput 的实例作为它的数据成员。每当我调用一个函数时,让我们说 setDetail(String),我已经设置了一个调用 publishEvent() 的建议。要调用 publishEvent(),我需要 DoStuff 类的 ServiceSAOServiceInput 实例。 问题是我如何从切面函数 (发布事件)?

DoStuff.java

public class DoStuff{
     @Autowired
     private ServiceSAO serviceSAO;
     private ServiceInput serviceInput;
     void init(){
          serviceSAO = new serviceSAO();
          serviceInput = ServiceUtil.getServiceInput(hostname,"test",....);
      }



  @PublishEventToService
     public void hello(){
          serviceInput.setDetail("batman");
      }

}

PublishEventToServiceAspect.java

@Aspect
public class PublishEventToServiceAspect{
       @After("execution(* com.xyz.ServiceInput.setDetails(..)) && @annotation(PublishEventToService)")
       public void publishEvent()
{
    String detail = serviceInput.getDetails();   //how can I get serviceInput here??
    someFuntion(serviceSAO, serviceInput);            //even tougher would be to get the serviceSAO instance??
}
}

编辑 1 请注意,我可以有各种 DoStuff 类。他们可能都想调用 publishEvent(),因此我需要一个通用方法来提取实例。

编辑 2 我现在可以使用 (ServiceInput)joinPoint.getTarget() 获取 ServiceInput 实例 有什么方法可以访问 ServiceSAO?

最佳答案

首先,免责声明:我对 AspectJ 了解很多,但对 Spring 知之甚少。所以我将在这里给出一个纯 AspectJ 的例子。

接下来,更正:您声称要访问 callee 成员,而 Serge Ballesta 的回答也是关于被叫方的 (target()捆绑)。但这与您在代码片段中描述的内容相矛盾,因为您不想访问与被调用方(ServiceInput 对象)相关的任何内容,而是访问调用方(DoStuff 对象)。对于调用者,您需要 this() 绑定(bind),而不是 target()

然后是另一个微妙的问题:您尝试访问的成员实际上是私有(private)的,即为了访问它们,您需要为它们提供公共(public) getter 或使用特权方面。我假设没有 setter/getter ,因为您的代码片段没有显示任何 setter/getter 。现在 AspectJ 中有一个限制:特权方面仅在 native AspectJ 语法中可用,not in annotation-style @AspectJ syntax .因此,我将在我的解决方案中使用 native 语法。如果您确实有公共(public) getter 或者可以轻松地将它们添加到您的类中,则可以将方面转换为 @AspectJ 语法,而无需特权访问。

我进一步假设您不限于“AOP lite”,即 Spring AOP,而是愿意使用成熟的 AspectJ(它也与 Spring 完全兼容)。

虚拟助手类:

package de.scrum_master.app;

public class ServiceSAO {
    private String id;
    public ServiceSAO(String id) { this.id = id; }
    @Override public String toString() { return "ServiceSAO [id=" + id + "]"; }
}

--

package de.scrum_master.app;

public class ServiceInput {
    private String id;
    private String detail;

    public ServiceInput(String id) { this.id = id; }
    public void setDetail(String detail) { this.detail = detail; }
    @Override public String toString() { return "ServiceInput [id=" + id + ", detail=" + detail + "]"; }
}

--

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface PublishEventToService {}

具有私有(private)成员和示例主要方法的应用程序类:

package de.scrum_master.app;

public class DoStuff {
    private ServiceSAO serviceSAO;
    private ServiceInput serviceInput;

    void init() {
        serviceSAO = new ServiceSAO("SAO");
        serviceInput = new ServiceInput("Input");
    }

    @PublishEventToService
    public void hello() {
        serviceInput.setDetail("batman");
    }

//  @PublishEventToService
    public void otherMethod() {
        serviceInput.setDetail("foobar");
    }

    public static void main(String[] args) {
        DoStuff doStuff = new DoStuff();
        doStuff.init();
        doStuff.hello();
        doStuff.otherMethod();
    }
}

请注意,hello() 有注解,otherMethod() 没有(注解被注释掉)。 IE。我们希望针对前者而不是后者触发建议。

访问私有(private)成员的特权方面:

package de.scrum_master.aspect;

import de.scrum_master.app.ServiceInput;
import de.scrum_master.app.PublishEventToService;
import de.scrum_master.app.DoStuff;

public privileged aspect PublishEventToServiceAspect {
    pointcut setDetail(DoStuff caller) :
        call(* ServiceInput.setDetail(..)) &&
        cflow(execution(@PublishEventToService * DoStuff+.*(..))) &&
        this(caller);

    after(DoStuff caller) : setDetail(caller) {
        System.out.println(thisJoinPointStaticPart);        
        System.out.println("  " + caller.serviceSAO);       
        System.out.println("  " + caller.serviceInput);     
    }
}

如您所见,我们在此处拦截 ServiceInput.setDetail(..) 的方法 call() 而不是 execution() 因为我们想在调用者的上下文中访问某些东西,而不是被调用者的。另请注意,call() 仅在 AspectJ 中可用,在 Spring AOP 中不可用,在 Spring AOP 中您仅限于 execution() 切入点。

我们还想确保只有在 DoStuff 类或其子类的任何当前执行的方法的控制流 (cflow()) 中的调用才会被拦截(因此 DoStuff+ 中的“+”)由 @PublishEventToService 注释。

最后但同样重要的是,我们要确保调用者 (this()) 被分配给一个名为 doStuffDoStuff 绑定(bind),它我们可以在我们的建议中使用。

如果所有这些条件都成立,让我们打印连接点和调用者的私有(private)成员以证明它确实有效。

示例输出:

execution(void de.scrum_master.app.DoStuff.hello())
  ServiceSAO [id=SAO]
  ServiceInput [id=Input, detail=batman]

这正是我们所期望的,也是您所要求的。现在,如果我们从 otherMethod() 的注释中删除注释,我们将有两个注释方法和方面打印:

execution(void de.scrum_master.app.DoStuff.hello())
  ServiceSAO [id=SAO]
  ServiceInput [id=Input, detail=batman]
execution(void de.scrum_master.app.DoStuff.otherMethod())
  ServiceSAO [id=SAO]
  ServiceInput [id=Input, detail=foobar]

如果您不理解或需要您之前未解释过的修改,请尽情享受并随时提问。

关于java - 从方面函数访问被调用者类的数据成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24259712/

相关文章:

java - 使用 Java 访问 CD-ROM

spring - 使用 SpringSocialConfigurer 时如何更改 Social AuthenticationFilter.filter Processes Url?

Java多线程在单元测试中不更新值

java - Aspectj 切入点表达式不适用于单个类中的一种方法,但适用于另一种方法

java - AspectJ 指示符 @args() 在 Spring AOP 中不起作用

aop - Ninject拦截扩展中的拦截接口(interface)方法

java - 提高设置 TextView RecyclerView 时的跨度/可跨度性能

java - 以多个下划线结尾的行

java - Java 中的消息格式以及如何在 Golang 中复制相同的格式

java - SQL状态[99999];错误代码 [17004];无效的列类型 : 1111 With Spring SimpleJdbcCall