我有一个DoStuff
类,其中包含ServiceSAO
和ServiceInput
的实例作为其数据成员。每当我调用一个函数时,可以说setDetail(String)
,我已经建立了一个调用publishEvent()
的建议。要调用publishEvent()
,我需要DoStuff类的ServiceSAO
和ServiceInput
个实例。
问题是我如何从方面函数(ServiceSAO
)访问被调用者类(ServiceInput
)的数据成员(DoStuff
和publishEvent
)?
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吗?
答案 0 :(得分:2)
首先,免责声明:我对AspectJ了解很多,但对Spring却知之甚少。所以我将在这里给出一个纯粹的AspectJ示例。
接下来,更正:您声称要访问 被调用者 成员,而Serge Ballesta的答案也是关于被调用者(target()
绑定)。但这与您在代码片段中描述的内容相矛盾,因为您不希望访问与被调用者(ServiceInput
对象)相关的任何内容,而是 调用者的成员(DoStuff
object)。对于呼叫者,您需要this()
绑定,而不是target()
。
然后是另一个微妙的问题:您尝试访问的成员实际上是私有的,即为了访问它们,您需要为他们提供公共getter或使用特权方面。我假设没有getter,因为你的代码片段没有显示任何内容。现在AspectJ存在一个限制:特权方面仅在本机AspectJ语法not in annotation-style @AspectJ syntax中可用。因此,我将在我的解决方案中使用本机语法。如果您确实有公共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 {}
具有私有成员和示例主要方法的应用程序类:
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()
未注释(注释已注释掉)。即我们希望为前者触发建议,而不是后者。
访问私有成员的特权方面:
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);
}
}
正如您所看到的,我们在call()
拦截ServiceInput.setDetail(..)
方法,而不是execution()
,因为我们想要访问来电者上下文中的某些内容,而不是被叫方'第另请注意,call()
仅适用于AspectJ,而不适用于限制为execution()
切入点的Spring AOP。
我们还希望确保只拦截当前执行的类cflow()
或其子类的任何控制流(DoStuff
)中的调用(因此" + "在DoStuff+
中注明@PublishEventToService
。
最后但并非最不重要的是,我们希望确保将调用方(this()
)分配给名为DoStuff
的{{1}}绑定,我们可以在建议中使用该绑定。
如果所有这些条件都成立,那么让我们打印加入点和来电者的私人成员,以证明它确实有效。
示例输出:
doStuff
这正是我们所期望的以及您所要求的。现在,如果我们从execution(void de.scrum_master.app.DoStuff.hello())
ServiceSAO [id=SAO]
ServiceInput [id=Input, detail=batman]
的注释中删除注释,我们有两个带注释的方法并打印方面:
otherMethod()
享受并随意询问您是否理解或需要您之前未解释过的修改。
答案 1 :(得分:0)
AspecJ提供this
或target
切入点指示符,它们可用作参数绑定。它们允许建议将目标对象(在您的情况下为serviceInput
)作为参数。
语法:
@After("execution(* com.xyz.ServiceInput.setDetails(..)) && @annotation(PublishEventToService)") && target(serviceInput)
public void publishEvent(ServiceInput serviceInput))
{
String detail = serviceInput.getDetails(); // should work fine
//someFuntion(serviceSAO, serviceInput); // but no use for serviceSAO instance
}
无论如何,你的服务应该是单身,所以你可以通过依赖注入来获得它们。