69

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>

但是当我的方法 2 被调用时,AOP 功能没有被调用,即checkAccess方法没有被调用 AOPLogger 类。

我有什么遗漏吗?

4

14 回答 14

89

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

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

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

Spring 文档(“了解 AOP 代理”部分)详细说明了这一点,以及一些解决方法(包括我上面的第一个建议)

于 2012-11-26T12:17:28.357 回答
38

2022 年更新:

现在我个人更喜欢使用这里描述的 TransactionHandler 类- 更清洁和灵活的方式。

原答案:

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

@Component
public class Foo {
    @Resource
    private Foo foo;
    
    public void method1(){
        ..
        foo.method2();
        ..
    }
    public void method2(){
        ..
    }
}

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

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

于 2016-08-30T14:48:27.420 回答
8

Spring AOP 框架是基于“代理”的,Understanding AOP Proxies的文档很好地解释了它。

当 Spring 构造一个配置了方面的 bean 时(例如您的示例中的“ABC”),它实际上创建了一个“代理”对象,该对象的行为就像真正的 bean。代理只是将调用委托给“真实”对象,但通过创建这种间接,代理有机会实现“建议”。例如,您的建议可以为每个方法调用记录一条消息。在此方案中,如果真实对象(“method1”)中的方法调用同一对象(例如,method2)中的其他方法,则这些调用在图片中没有代理的情况下发生,因此它没有机会实现任何建议。

在您的示例中,当调用 method1() 时,代理将有机会执行它应该执行的操作,但如果 method1() 调用 method2(),则图片中没有任何方面。但是,如果从其他 bean 调用 method2,代理将能够执行通知。

于 2012-11-26T19:54:12.633 回答
6

我遇到了同样的问题,我通过实现 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();
    ...........
  }

希望这可以帮助。

于 2013-09-19T14:38:17.210 回答
3

使用@Autowired它有效。而不是调用内部方法 as this.method(),你可以这样做:

@Autowired
Foo foo;

然后调用:

foo.method2();
于 2017-10-11T20:04:54.087 回答
0

你想要达到的目标是不可能的。Spring Reference Documentation中有解释。

于 2012-11-26T12:17:45.677 回答
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 上下文与 AOP 相悖。在创建代理时,它还需要一些额外的配置。

于 2018-09-10T07:05:50.560 回答
0

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

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

于 2018-11-27T10:11:21.307 回答
0

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

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

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

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

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

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

于 2019-03-01T19:54:11.203 回答
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();
    -------
    -------
   }

}
于 2019-08-04T04:16:02.793 回答
0

你可以参考这个问题:https ://stackoverflow.com/a/30611671/7278126

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

于 2019-12-07T14:23:48.423 回答
0

经过大量研究,我发现以下内容就像一个魅力。但是 ASPECTJ WEAVING 总是有更好的方法。在本质上使用自引用。

@Autowired
private ApplicationContext applicationContext;
private <<BEAN>> self;

请注意 <> 属于同一类,需要使用 AOP 记录或用于其他用途。

@PostConstruct
private void init() {
    self = applicationContext.getBean(MyBean.class);
}

有了这个改变,你所要做的就是移动对

this.methodName(args) -> self.methodName(args)

希望这可以帮助所有想要在应用程序中使用性能日志记录解决方案的人。

于 2020-05-08T09:52:49.160 回答
0

你可以这样做:

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

ABC self;

public void method1(){

   .........

   self.method2();

  ...........

}


public void method2(){

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

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

}
于 2021-02-02T10:55:18.317 回答
-1

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

@Component
public class ABC {

    @Resource
    private ABC self = this;

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

    public void method2() {

    }

}
于 2018-11-27T04:08:09.517 回答