一个关于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
。
代码
<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
。
相关文章推荐
- 关于mule中Spring使用中的一个问题
- Spring中关于classpath:和classpath*:前缀的一个小问题
- [原创]java WEB学习笔记104:Spring学习---AOP 前奏,通过一个问题引入动态代理
- 关于Spring Aop存在的一点问题的思考
- 关于Spring boot打war包部署在tomcat上的一个问题
- spring-boot中关于Maven依赖管理的一个问题
- 在同一个类中调用另一个方法没有触发 Spring AOP 的问题
- 关于Spring中AOP的一个小例子程序
- 在同一个类中调用另一个方法没有触发 Spring AOP 的问题【转】
- 关于spring restful使用中遇到的一个性能问题
- 关于Castle中AOP的一个问题,急!!
- Spring AOP代理机制以及方法调用在同一个类的另一个方法的问题
- 在同一个类中调用另一个方法没有触发 Spring AOP 的问题
- 工作中遇到的一个相当有意思的问题(关于Windows和linux环境下执行PHP,ajax,javascript,flexigrid的一个莫名异常)
- 关于Spring事务的一个问题
- 关于spring中aop的一个入门案例
- 关于使用Spring AOP 环绕性增强出现异常影响业务流程的问题
- 关于spring3使用AOP编程时需要引入哪些jar包的问题
- 一个神奇Spring AOP的java.lang.ClassCastException问题
- Javaweb-------mybatis+springmvc关于实体类使用注解的一个问题