我有一个 DoStuff
类,它有 ServiceSAO
和 ServiceInput
的实例作为它的数据成员。每当我调用一个函数时,让我们说 setDetail(String)
,我已经设置了一个调用 publishEvent()
的建议。要调用 publishEvent()
,我需要 DoStuff 类的 ServiceSAO
和 ServiceInput
实例。
问题是我如何从切面函数 (发布事件
)?
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()
) 被分配给一个名为 doStuff
的 DoStuff
绑定(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/