对于具有接口的类,Spring AOP是无能为力的

时间:2018-10-03 06:53:51

标签: java spring inheritance aop spring-aop

我了解到Spring AOP的能力非常有限(它只能切入Spring Bean类的公共方法,并且只能从类外部调用这些方法)。但是现在我发现了涉及接口时的另一个莫名其妙的局限性。

通常,如果一个类是子类,则Spring AOP切入其所有方法(即使是被覆盖的方法)也没有问题:

public class A {
    public void methodA() { } //OK, can cut in
}

public class B extends A {
    @Override
    public void methodA() { } //OK, can cut in

    public void methodB() { } //OK, can cut in
}

但是,当我们在混合中添加接口时,对于Spring AOP来说事情变得很糟糕:

public interface I {
    public void methodA();
}

public class A implements I {
    @Override
    public void methodA() { } //OK, can cut in

    public void methodB() { } //Fail, cannot see or cut into this method
}

public class B extends A {
    @Override
    public void methodA() { } //Fail, cannot see or cut into this method

    public void methodC() { } //Fail, cannot see or cut into this method
}

首先,Spring AOP只能切入接口中的方法,其他任何方法都无法看到。其次,它只能切入直接实现接口方法的方法-A.methodA()。它不能切入被B覆盖的相同方法。

我正在使用通用切入点表达式"execution(* method*(..))"来切入所有可能的方法,因此这不是表达式问题。

有什么办法可以解决这个限制?还是我应该忘记Spring AOP并使用其他方法?

更新: 好的,我已经找到了问题的真正原因。我实际上是依靠Intellij IDEA的AOP插件来测试的。应该将切入点链接到所有受影响的方法。但是它使用的是“旧的”动态JDK代理策略,而不是新的CGLIB策略。因此,它并不是将其链接到所有方法,但是当我实际运行程序时,它将正确地切入所有方法。

我正在使用Spring Boot 2,它使用了“新” CGLIB策略。但是在SB1上,它可能仍然使用“旧的”动态JDK代理策略,因此它可能在那里仍然不起作用。

1 个答案:

答案 0 :(得分:5)

Spring将使用动态代理或cglib来实现AOP。

如果没有接口,则选择Cglib,它将有效地创建目标类的子类,并覆盖目标类中的所有方法。通过这种方式,除了最终方法和静态方法之外,所有方法都可以切入。

如果目标类具有接口,则Spring可以使用其中一个接口使用动态代理,并且显然这只会影响接口中声明的方法。

在spring-boot 2.0之前,动态代理是默认策略。现在,Cglib是spring-boot 2.0之后的默认策略。

在我看来,您的情况似乎可以采用动态代理方法。您可以在application.yaml中添加 spring.aop.proxy-target-class:true 来强制使用Cglib。

如果仍然有问题,最好发布更完整的代码片段以显示如何调用方法。