【问题标题】:Use of proxies in Spring AOPSpring AOP 中代理的使用
【发布时间】:2012-01-03 16:46:19
【问题描述】:
我正在读一本书,其中谈到在 Spring AOP 中启用 AspectJ 支持。
下面是从书中摘录的一段:
要在 Spring IoC 容器中启用 AspectJ 注释支持,您只需定义一个空的
bean 配置文件中的 XML 元素 aop:aspectj-autoproxy。然后,Spring 会自动
为与 AspectJ 方面匹配的任何 bean 创建代理。
对于接口在应用程序设计中不可用或未使用的情况,可以
通过依赖CGLIB创建代理。要启用CGLIB,需要在<aop:aspectj-autoproxy />中设置属性proxy-target-class=true。
我无法得到第二段。 '接口不可用'是什么意思。谁能用一个例子来说明这一点?
【问题讨论】:
标签:
java
spring
aop
aspectj
【解决方案1】:
Spring AOP 使用 JDK 动态代理或 CGLIB 为您的目标对象创建代理。
根据 Spring 文档,如果您的目标实现了至少一个接口,则将使用 JDK 动态代理。但是,如果您的目标对象没有实现任何接口,则将创建一个 CGLIB 代理。
这是强制创建 CGLIB 代理的方法(设置 proxy-target-class="true"):
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>
当使用 AspectJ 及其自动移植支持时,您还可以强制使用 CGLIB 代理。这是使用<aop:aspectj-autoproxy> 的地方,而且这里的“proxy-target-class”必须设置为 true:
<aop:aspectj-autoproxy proxy-target-class="true"/>
有关详细信息,请参阅Aspect Oriented Programming with Spring 文档的代理机制部分。
【解决方案2】:
Spring 更喜欢为 AOP 使用接口,因为它可以使用 JDK proxies。
比如说我有一个接口MyService
public interface MyService {
void doSomething();
}
还有一个实现MyServiceImpl
@Service
public class MyServiceImpl implements MyService {
public void doSomething() {
// does something!
}
}
如果 Spring 发现您已经为 MyService 配置了方面,它将创建一个实现 MyService 的 JDK 代理,然后将所有调用代理到您的 MyServiceImpl bean,并在适当的情况下添加方面功能。
JDK 代理通过实现与目标对象相同的接口并将调用委托给它来工作;如果没有要实现的接口,它们就不起作用。如果你没有像上面这样的接口,Spring 需要使用像 CGLIB 这样的字节码库在运行时动态创建包含切面功能的类。
【解决方案3】:
我找到了一个博客 here,它清楚地解释了 AOP、缓存和事务如何使用运行时代理类工作。
当不编码接口时(引用博客的部分'如果 bean 类没有实现任何接口怎么办?'):-
默认情况下,如果你的 bean 没有实现接口,Spring 使用
技术继承:在启动时,会创建一个新类。它
从您的 bean 类继承并在子方法中添加行为。
为了生成这样的代理,Spring 使用了第三方库
称为 cglib。
【解决方案4】:
Spring AOP 广泛使用代理作为以非侵入方式实现横切关注点(也称为方面)的机制,其想法基本上是使用代理作为丰富原始行为的包装器,即添加事务功能。
要实现这一点,有两种选择,具体取决于原始对象是否实现接口。
在第一种情况下(原始对象至少实现一个接口)反射 API 的动态代理功能用于创建一个代理对象,该对象实现与原始对象相同的接口,因此可以使用代理代替。
在第二种情况下(原始对象确实不实现任何接口),因此必须使用更详细的技巧,这就是CGLIB出现的时候。根据项目页面“CGLIB 用于扩展 Java 类并在运行时实现接口”。所以在这种情况下,技巧在于创建一个EXTENDS原始对象的代理,因此可以使用它。