Java 代码
- <br>public class UserDAOImpl{
- <br><br> public void save() {
- <br> // TODO Auto-generated method stub
- <br> System.out.println("user saved");
- <br> }
- <br>}
- <br>// 相关配置,省略了一些不相关内容
- <br><bean id="userDAO" class="UserDAOImpl">
- <br><bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <br> <property name="target">
- <br> <ref local="userDAO" />
- <br> </property>
- <br></bean>
测试代码
Java 代码
- ApplicationContext ctx =
- <br> new FileSystemXmlApplicationContext("applicationContext.xml");
- <br> UserDAOImpl userDAOImpl =
- <br> (UserDAOImpl)ctx.getBean("userDAOProxy");
- <br> userDAOImpl.save();
上面这种情况下程序可以正常运行,但是如果UserDAOImpl 实现了一个接口,其他不变
Java 代码
- public class UserDAOImpl implements UserDAO {
- <br>
- <br> public void save() {
- <br> // TODO Auto-generated method stub
- <br> System.out.println("user saved");
- <br> }
- <br>
- <br>}
这种情况下,程序将不能正常运行,会抛出java.lang.ClassCastException 异常
理解上面这种情况产生的原因需要了解Spring AOP 的实现原理。
Spring 实现AOP 是依赖JDK 动态代理和CGLIB 代理实现的。
以下是JDK 动态代理和CGLIB 代理简单介绍
JDK 动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB 代理:实现原理类似于JDK 动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB 是高效的代码生成包,底层是依靠ASM (开源的java 字节码编辑类库)操作字节码实现的,性能比JDK 强。
Spring 是依靠什么来判断采用哪种代理策略来生成AOP 代理呢?以下代码就是Spring 的判断逻辑
Java 代码
- //org.springframework.aop.framework.DefaultAopProxyFactory
- <br> // 参数AdvisedSupport 是Spring AOP 配置相关类
- <br> public AopProxy createAopProxy(AdvisedSupport advisedSupport)
- <br> throws AopConfigException {
- <br> // 在此判断使用JDK 动态代理还是CGLIB 代理
- <br> if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()
- <br> || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {
- <br> if (!cglibAvailable) {
- <br> throw new AopConfigException(
- <br> "Cannot proxy target class because CGLIB2 is not available. "
- <br> + "Add CGLIB to the class path or specify proxy interfaces.");
- <br> }
- <br> return CglibProxyFactory.createCglibProxy(advisedSupport);
- <br> } else {
- <br> return new JdkDynamicAopProxy(advisedSupport);
- <br> }
- <br> }
advisedSupport.isOptimize() 与advisedSupport.isProxyTargetClass() 默认返回都是false ,所以在默认情况下目标对象有没有实现接口决定着Spring 采取的策略,当然可以设置advisedSupport.isOptimize() 或者advisedSupport.isProxyTargetClass() 返回为true ,这样无论目标对象有没有实现接口Spring 都会选择使用CGLIB 代理。所以在默认情况下,如果一个目标对象如果实现了接口Spring 则会选择JDK 动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出j ava.lang.ClassCastException 。而所以在默认情况下,如果目标对象没有实现任何接口,Spring 会选择CGLIB 代理, 其生成的动态代理对象是目标类的子类。
以上说的是默认情况下,也可以手动配置一些选项使 Spring 采用 CGLIB 代理。
org.springframework.transaction.interceptor.TransactionProxyFactoryBean 是 org.springframework.aop.framework. ProxyConfig 的子类,所以可以参照 ProxyConfig 里的一些设置如下所示,将 optimize 和 proxyTargetClass 任意一个设置为 true 都可以强制 Spring 采用 CGLIB 代理。
Java 代码
- // 相关配置,省略了一些不相关内容
- <br><bean id="userDAO" class="UserDAOImpl">
- <br><bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <br> <property name="target">
- <br> <ref local="userDAO" />
- <br> </property>
- <br> <property name="optimize">
- <br> <value>true</value>
- <br> </property>
- <br> <property name="proxyTargetClass">
- <br> <value>true</value>
- <br> </property>
- <br></bean>
使用 CGLIB 代理也就不会出现前面提到的ClassCastException 问题了,
也可以在性能上有所提高,但是也有它的弊端, Spring doc 原文解释如下 optimization will usually mean that advice changes won't take effect after a proxy has been created. For this reason, optimization is disabled by default 。