您的位置:首页 > 编程语言 > Java开发

一个关于Spring AOP很有意思的问题。

2011-03-03 19:20 295 查看
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>

public class UserDAOImpl{

public void save() {

// TODO Auto-generated method stub

System.out.println("user
saved");

}

}

//
相关配置,省略了一些不相关内容

<bean id="userDAO" class="UserDAOImpl">

<bean id="userDAOProxy"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property
name="target">

<ref local="userDAO"
/>

</property>

</bean>

测试代码

Java
代码

ApplicationContext ctx =

<br> new FileSystemXmlApplicationContext("applicationContext.xml");

<br> UserDAOImpl userDAOImpl =

<br> (UserDAOImpl)ctx.getBean("userDAOProxy");

<br> userDAOImpl.save();

ApplicationContext ctx =

new
FileSystemXmlApplicationContext("applicationContext.xml");

UserDAOImpl userDAOImpl =

(UserDAOImpl)ctx.getBean("userDAOProxy");

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>}

public class UserDAOImpl
implements UserDAO {

public void save() {

// TODO Auto-generated method stub

System.out.println("user
saved");

}

}

这种情况下,程序将不能正常运行,会抛出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> }

//org.springframework.aop.framework.DefaultAopProxyFactory

//
参数AdvisedSupport
是Spring AOP
配置相关类

public AopProxy
createAopProxy(AdvisedSupport advisedSupport)

throws AopConfigException {

//
在此判断使用JDK
动态代理还是CGLIB
代理

if (advisedSupport.isOptimize() ||
advisedSupport.isProxyTargetClass()

||
hasNoUserSuppliedProxyInterfaces(advisedSupport)) {

if (!cglibAvailable) {

throw new AopConfigException(

"Cannot proxy target class
because CGLIB2 is not available. "

+ "Add CGLIB to the class
path or specify proxy interfaces.");

}

return
CglibProxyFactory.createCglibProxy(advisedSupport);

} else {

return new
JdkDynamicAopProxy(advisedSupport);

}

}

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>

//
相关配置,省略了一些不相关内容

<bean id="userDAO" class="UserDAOImpl">

<bean id="userDAOProxy"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property
name="target">

<ref local="userDAO"
/>

</property>

<property
name="optimize">

<value>true</value>

</property>

<property
name="proxyTargetClass">

<value>true</value>

</property>

</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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: