【问题标题】:Why in Spring AOP the object are wrapped into a JDK proxy that implements interfaces?为什么在 Spring AOP 中对象被包装到实现接口的 JDK 代理中?
【发布时间】:2015-06-21 10:06:58
【问题描述】:

我正在学习 Spring,我有以下内容

考虑以下 bean 定义:

<bean id="clientService" class="com.myapp.service.ClientServiceImpl" />

现在考虑将其声明为 pointcut* 的情况,该切入点针对 **clientService bean 中的所有方法。

还要考虑 ClientServiceImpl实现 3 个接口

现在我知道使用 AOP 代理了 clientService bean,并且该代理实现了所有 3 个接口。

但是实现这三个接口的确切原因是什么?

所以在我看来存在 2 种代理(如果我说的断言错误,请纠正我):

  1. JDK 代理:默认情况下从 Spring 中使用(是真的吗?)我有一个接口来定义我想要代理的对象的方法。所以这个接口的具体实现是被代理包裹起来的。所以当我在我的对象上调用一个方法时,我是在它的代理上调用它。 方法拦截器会识别调用,该方法最终会执行方面,然后执行调用的方法。

  2. CGLIB 代理:在我看来,代理扩展了包装对象的实现,并为其添加了额外的逻辑功能

类似这样的:

所以在我看来,Spring 使用了第一种基于接口实现的代理(对吗?):

我认为在 AOP 中,额外的逻辑由 方法拦截器 的实现表示(是真的吗?),标准逻辑由接口中定义的方法。

但是,如果前面的推理是正确的,我的疑惑是:为什么我需要定义这些接口,而被对象包裹的对象要实现这些接口呢? (我不明白代理本身是否实现了这些接口)。

为什么?具体是如何工作的?

Tnx

【问题讨论】:

    标签: java spring design-patterns aop spring-aop


    【解决方案1】:

    但是这三个界面的确切原因是什么 实施了吗?

    如果代理没有实现所有这些接口,则 bean 无法连接到使用该接口的其他 bean(您会得到 ClassCastException)。例如,将该接口的所有 bean 自动装配到一个 bean 中。此外,如果代理没有实现该接口,getBeanNamesForType 之类的东西将无法工作。

    所以在我看来存在 2 种代理(如果我是,请纠正我 说错误的断言)

    是的,这是正确的。见ScopedProxyMode。默认情况下,Spring 不会创建代理。它仅在需要包装 bean 以添加附加行为 (AOP) 时才创建代理。请注意,还有 a special case of the CGLIB based proxy 使用 Objenesis 来处理没有默认构造函数的子类化目标。

    CGLIB 代理:在我看来,代理扩展了 包装对象的实现增加了额外的逻辑 特点

    当您使用基于 CGLIB 的代理时,您的 bean 的构造函数会被调用两次:一次是在实例化动态生成的子类时(创建代理),第二次是在创建实际的 bean 时(目标)。

    我认为在 AOP 中,额外的逻辑由 方法拦截器的实现(是真的吗?)

    代理本质上只是调用需要应用的建议链。该建议并未在代理本身中实现。例如,@Transactional 的建议位于 TransactionAspectSupport。看源码到JdkDynamicAopProxy

    标准逻辑由 方法定义到接口中。

    假设您正在针对接口进行编程并使用正确的 JDK 代理。

    但是,如果前面的推理是正确的,我的疑问是:为什么我需要 定义这些接口并执行由对象包裹的对象 实现这些接口? (我不明白代理本身是否 实现这些接口)。

    如果您想使用基于接口的代理,您需要使用接口。只需确保所有 bean 都实现接口,所有建议的方法都由这些接口定义,并且当一个 bean 依赖于另一个 bean 时,该依赖关系是使用接口指定的。 Spring 将负责构建代理并确保它实现所有接口。

    在您的图表中,您有“Spring AOP 代理 (this)”。当您使用任何类型的代理时,您必须非常小心使用this

    1. 同一类中的调用不会应用建议,因为这些调用不会通过代理。
    2. 如果您在其中一个 bean 中将 this 传递给某些外部代码,则您传递了 AOP 建议的目标。如果其他代码使用该引用,则调用不会应用 AOP 建议(同样,您正在绕过代理)。

    【讨论】:

    • 我同意,这是一个很好的解释。顺便说一句,没有明确提到并且对其他读者来说可能并不明显的是,如果我们可以只使用 Java 动态代理,为什么 CGLIB 是必要的:因为一些开发人员并不总是使用接口,而只为他们的组件提供实现。但是 JDK 动态代理仅适用于接口。这就是CGLIB发挥作用的地方。它动态地创建子类。因此这两种技术。 P.S.:如果你想摆脱这两种类型的代理,只需使用 AspectJ 而不是 Spring AOP。它更加高效和强大。
    最近更新 更多