Spring AOP 对 类 接口无能为力

Spring AOP is powerless for classes with interfaces

我知道 Spring AOP 的能力非常有限(它只能切入 class 的 public 方法,这些方法是 Spring bean,并且只能当从 class 之外调用这些方法时)。但现在我发现了另一个令人费解的接口限制。

通常,如果 class 被子classed,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 插件来测试这个。它应该 link 所有受影响方法的切入点。但它使用的是 'old'、动态 JDK 代理策略,而不是新的 CGLIB 策略。所以它并没有 link 将它应用于所有方法,但是当我实际上 运行 我的程序时,它会正确地切入所有方法。

我正在使用 Spring Boot 2,它使用 'new' CGLIB 策略。但是在 SB1 上它可能仍然使用 'old' 动态 JDK 代理策略,所以它可能仍然无法在那里工作。

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

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

如果目标 class 带有接口,那么 Spring 可能会使用其中一个接口使用动态代理,显然这只会影响接口中声明的方法。

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

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

如果您仍然有问题,最好 post 更完整的代码片段显示如何调用这些方法。