ABC.java中定义了两种方法
public void method1(){
.........
method2();
...........
}
public void method2(){
...............
...............
}
我希望在方法2的调用上有AOP。所以,我创建了一个类AOPLogger.java,在方法checkAccess中提供了方面功能 在配置文件中,我做了类似下面的事情
<bean id="advice" class="p.AOPLogger" />
<aop:config>
<aop:pointcut id="abc" expression="execution(*p.ABC.method2(..))" />
<aop:aspect id="service" ref="advice">
<aop:before pointcut-ref="abc" method="checkAccess" />
</aop:aspect>
</aop:config>
但是当调用我的method2时,不会调用AOP功能,即不会调用checkAccess方法的AOPLogger类。
我错过了什么?
该方面应用于围绕bean的代理。请注意,每次获得对bean的引用时,它实际上不是您的配置中引用的类,而是实现相关接口的合成类,委托给实际类并添加功能,例如您的AOP。
在上面的示例中,您直接在类上调用,而如果该类实例作为Spring bean注入另一个类,则将其作为代理注入,因此将在代理上调用方法调用(并且将触发方面)
如果要实现上述目标,可以将method1
/ method2
拆分为单独的bean,或者使用非弹簧导向的AOP框架。
Spring doc (section "Understanding AOP Proxies")详细介绍了这个,以及几个解决方法(包括我上面的第一个建议)
我很惊讶没有人提到这个,但我认为我们可以使用Spring提供的ControlFlowPointcut。
ControlFlowPointcut仅在堆栈跟踪中找到特定方法时才查看堆栈跟踪并匹配切入点。只有在特定上下文中调用方法时,才会匹配切入点。
在这种情况下,我们可以创建一个切入点
ControlFlowPointcut cf = new ControlFlowPointcut(MyClass.class, "method1");
现在,使用ProxyFactory在MyClass实例上创建代理并调用method1()。
在上面的例子中,只有method2()会被建议,因为它是从method1()调用的。
另一种方法是将method2()模式化为其他类文件,并使用@Component注释该类。然后在需要时使用@Autowired注入它。这样AOP可以拦截它。
例:
You were doing this...
Class demo{
method1(){
-------
-------
method2();
-------
-------
}
method2(){
------
-----
}
}
Now if possible do this :
@Component
class NewClass{
method2(){
------
-----
}
}
Class demo{
@AutoWired
NewClass newClass;
method1(){
-------
-------
newClass.method2();
-------
-------
}
}
它可以通过自我注射使用来完成。您可以通过注入实例调用内部方法:
@Component
public class Foo {
@Resource
private Foo foo;
public void method1(){
..
foo.method2();
..
}
public void method2(){
..
}
}
从Spring 4.3开始,你也可以使用@Autowired来完成它。
从4.3开始,@ Autowired还考虑了自我引用注入,即引用回到当前注入的bean。
我有同样的问题,我通过实现Spring的ApplicationContextAware
,BeanNameAware
并实现如下相应的方法来克服。
class ABC implements ApplicationContextAware,BeanNameAware{
@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
applicationContext=ac;
}
@Override
public void setBeanName(String beanName) {
this.beanName=beanName;
}
private ApplicationContext applicationContext;
private String beanName;
}
然后我在调用同一类的方法的同时用this.
替换了((ABC) applicationContext.getBean(beanName)).
。这可确保仅通过代理调用同一类的方法。
所以method1()
改为
public void method1(){
.........
((ABC) applicationContext.getBean(beanName)).method2();
...........
}
希望这可以帮助。
Spring AOP框架是基于“代理”的,它在这里得到了很好的解释:http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies
当Spring构造一个配置了方面的bean(在你的例子中就像“ABC”)时,它实际上创建了一个“代理”对象,就像真正的bean一样。代理只是将调用委托给“真实”对象,但通过创建这种间接,代理有机会实现“建议”。例如,您的建议可以为每个方法调用记录一条消息。在这个方案中,如果真实对象中的方法(“method1”)调用同一个对象中的其他方法(比如,method2),那些调用在图片中没有代理就会发生,因此它没有机会实现任何建议。
在您的示例中,当调用method1()时,代理将有机会执行它应该执行的操作但是如果method1()调用method2(),则图片中没有方面。但是,如果从其他bean调用method2,代理将能够执行建议。
希望这可以帮助。
谢谢,Raghu
使用@Autowired
它的工作原理。您可以执行以下操作,而不是将内部方法称为this.method()
:
@Autowired
Foo foo;
然后打电话:
foo.method2();
你想要实现的目标是不可能的。解释是在Spring Reference Documentation。
如Spring docs章节5.6.1了解AOP代理中所示,您可以采用另一种方法:
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
虽然作者不推荐这种方式。因为:
这完全将您的代码耦合到Spring AOP,它使类本身意识到它正在AOP上下文中使用,它在AOP面前飞行。在创建代理时,还需要一些额外的配置。
您可以通过这种方式进行自我注入,以便可以在Spring应用程序之外使用此类。
@Component
public class ABC {
@Resource
private ABC self = this;
public void method1() {
self.method2();
}
public void method2() {
}
}
使用@EnableAspectJAutoProxy(exposeProxy = true)注释调用,并使用((Class)AopContext.currentProxy())调用实例方法.method();
严格不推荐这样做,因为它增加了耦合