Spring AOP不能在另一个方法内调用方法

时间:2012-11-26 12:13:22

标签: java spring spring-aop

ABC.java

中定义了两种方法
public void method1(){
   .........
   method2();
  ...........
}


public void method2(){
  ...............
  ...............  
}

我希望在 method2 的电话中使用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类调用。

我错过了什么?

13 个答案:

答案 0 :(得分:56)

该方面适用于围绕bean的代理。请注意,每次获得对bean的引用时,它实际上不是配置中引用的类,而是实现相关接口的合成类,委托给实际类并添加功能,例如AOP。

在上面的示例中,您在类上直接调用 ,而如果将该类实例作为Spring bean注入到另一个类中,则将其作为代理注入,因此将调用方法调用在代理上(并且将触发方面)

如果要实现上述目标,可以将method1 / method2拆分为单独的bean,或使用非弹簧导向的AOP框架。

Spring doc (section "Understanding AOP Proxies")详细介绍了这一点,以及几个解决方法(包括我上面的第一个建议)

答案 1 :(得分:30)

可以通过自我注射使用来完成。您可以通过注入实例调用内部方法:

@Component
public class Foo {
    @Resource
    private Foo foo;

    public void method1(){
        ..
        foo.method2();
        ..
    }
    public void method2(){
        ..
    }
}

从Spring 4.3开始,你也可以使用@Autowired。

  

从4.3开始,@ Autowired还考虑自我注射,   即引用回到当前注入的bean。

答案 2 :(得分:5)

我遇到了同样的问题,我通过实施Spring ApplicationContextAwareBeanNameAware并实施相应的方法来克服这些问题。

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();
    ...........
  }

希望这有帮助。

答案 3 :(得分:3)

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

答案 4 :(得分:2)

使用@Autowired它有效。 您可以执行以下操作,而不是将内部方法调用为this.method()

@Autowired
Foo foo;

然后致电:

foo.method2();

答案 5 :(得分:0)

你想要实现的目标是不可能的。解释在Spring Reference Documentation

答案 6 :(得分:0)

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却是这样。创建代理时,还需要一些其他配置。

答案 7 :(得分:0)

您可以通过这种方式进行自我注入,以便此类可以在Spring应用程序之外使用。

@Component
public class ABC {

    @Resource
    private ABC self = this;

    public void method1() {
        self.method2();
    }

    public void method2() {

    }

}

答案 8 :(得分:0)

使用@EnableAspectJAutoProxy(exposeProxy = true)注释调用并使用((Class)AopContext.currentProxy())。method();调用实例方法

不建议这样做,因为它会增加耦合

答案 9 :(得分:0)

我很惊讶没有人提到这一点,但是我认为我们可以使用Spring提供的ControlFlowPointcut。

ControlFlowPointcut会查看stacktrace并仅在它在stacktrace中找到特定方法时才与切入点匹配。本质上,切入点只有在特定上下文中调用方法时才匹配。

在这种情况下,我们可以像

一样创建切入点。
ControlFlowPointcut cf = new ControlFlowPointcut(MyClass.class, "method1");

现在,使用ProxyFactory在MyClass实例上创建一个代理,然后调用method1()。

在上述情况下,仅建议使用method2(),因为它是从method1()调用的。

答案 10 :(得分:0)

另一种解决方法是将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();
    -------
    -------
   }

}

答案 11 :(得分:0)

您可以参考以下问题:https://stackoverflow.com/a/30611671/7278126

这是一种解决方法,但可以解决问题,这里的关键是使用相同的bean(代理)来代替“ method2”来调用。

答案 12 :(得分:0)

你可以这样做:

@Autowired // to make this bean refers to proxy class 

ABC self;

public void method1(){

   .........

   self.method2();

  ...........

}


public void method2(){

  ...............

  ...............  

}