【问题标题】:When is CGLIB proxy used by Spring AOP?Spring AOP 何时使用 CGLIB 代理?
【发布时间】:2018-08-11 00:45:48
【问题描述】:

我在阅读一些关于 Spring AOP 的文章时遇到了这个问题:

AOP代理:AOP创建的实现切面的对象 合同。在 Spring 中,代理对象可以是 JDK 动态代理或 CGLIB 代理。默认情况下,代理对象将是 JDK 动态的 代理,并且被代理的对象必须实现一个接口, 这也将由代理对象实现。但是像这样的图书馆 CGLIB 也可以通过子类化来创建代理,所以 不需要接口。

你能不能看看下面的结构,想象一下我们要建议bar()方法。

public interface Foo {
    void foo();
}

public class FooImpl implements Foo {

    @Override
    public void foo() {
        System.out.println("");
    }

    public void bar() {
        System.out.println("");
    }

}

这是否意味着在这种情况下将使用 CGLIB 代理? 由于JDK动态代理无法实现任何接口来覆盖bar()方法。

【问题讨论】:

  • 这取决于——你是如何注入 bean 的?

标签: java spring proxy spring-aop cglib


【解决方案1】:

Spring 只会在您告诉它的情况下使用 CGLIB。这通过将@EnableAspectJAutoProxyproxyTargetClass 元素设置为true 来启用(对于基于注释的配置)。

@EnableAspectJAutoProxy(proxyTargetClass = true)

考虑这个最小的例子(假设你的FooImpl@Component 注释)

@Aspect
@Component
class FooAspect {
    @Before("execution(public void bar())")
    public void method() {
        System.out.println("before");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
        FooImpl f = ctx.getBean(FooImpl.class); // throw exception here
        f.bar();
    }
}

默认情况下,proxyTargetClassfalse。在这种情况下,Spring 不会使用 CGLIB。由于 @Aspect 类中的 @Before 建议,Spring 将决定它需要使用 JDK 代理来代理 FooImpl。不幸的是,由于这种代理行为,实际存储在上下文中的 bean 将是动态 JDK Proxy 类型(也是 Foo 接口的子类型),因此尝试使用 FooImpl.class 获取 bean 将失败。

即使您尝试将其检索为Foo,您也无法调用bar() 方法,因为代理对象不是FooImpl

如果启用proxyTargetClass,上述代码将按预期工作,创建CGLIB 代理,并调用@Before 建议。

【讨论】:

  • 谢谢,但是你能解释一下为什么你的sn-p会抛出异常吗?如果我们无法检索/调用它,建议使用 JDK 动态代理的方法的目的是什么?
  • @Pavel 虽然FooImpl@Compoent 注释,但由于建议,它也是代理的目标。因此,Spring 将为它创建一个代理类型的 bean 定义,因此不是 FooImpl
【解决方案2】:

请参阅 Spring 文档中的 AOP Proxies

Spring AOP 默认为 AOP 代理使用标准的 JDK 动态代理。这使得任何接口(或一组接口)都可以被代理。

Spring AOP 也可以使用 CGLIB 代理。这是代理类而不是接口所必需的。默认情况下,如果业务对象没有实现接口,则使用 CGLIB。

【讨论】:

    【解决方案3】:

    Spring AOP 默认为 AOP 代理使用标准的 JDK 动态代理。这使得任何接口(或一组接口)都可以被代理。

    Spring AOP 也可以使用 CGLIB 代理。这是代理类而不是接口所必需的。如果业务对象未实现接口,则默认使用 CGLIB。因为对接口而不是类进行编程是一种好习惯;业务类通常会实现一个或多个业务接口。在那些(希望很少见)需要建议未在接口上声明的方法或需要将代理对象作为具体类型传递给方法的情况下,可以强制使用 CGLIB。

    了解 Spring AOP 是基于代理的这一事实很重要。请参阅了解 AOP 代理,以全面了解此实现细节的实际含义。

    https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-introduction

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-19
      • 1970-01-01
      • 2012-01-03
      • 2011-11-30
      相关资源
      最近更新 更多