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

问题描述 投票:43回答:10

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类。

我错过了什么?

java spring spring-aop
10个回答
59
投票

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

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

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

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


0
投票

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

ControlFlowPointcut仅在堆栈跟踪中找到特定方法时才查看堆栈跟踪并匹配切入点。只有在特定上下文中调用方法时,才会匹配切入点。

在这种情况下,我们可以创建一个切入点

ControlFlowPointcut cf = new ControlFlowPointcut(MyClass.class, "method1");

现在,使用ProxyFactory在MyClass实例上创建代理并调用method1()。

在上面的例子中,只有method2()会被建议,因为它是从method1()调用的。


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

}

33
投票

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

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

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

从Spring 4.3开始,你也可以使用@Autowired来完成它。

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


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
投票

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


2
投票

使用@Autowired它的工作原理。您可以执行以下操作,而不是将内部方法称为this.method()

@Autowired
Foo foo;

然后打电话:

foo.method2();

0
投票

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


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面前飞行。在创建代理时,还需要一些额外的配置。


0
投票

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

@Component
public class ABC {

    @Resource
    private ABC self = this;

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

    public void method2() {

    }

}

0
投票

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

严格不推荐这样做,因为它增加了耦合

© www.soinside.com 2019 - 2024. All rights reserved.