zoukankan      html  css  js  c++  java
  • 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。

     

    一、基础工作

    首先修改我们上一次做的 SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结,如下所示:

        将xml声明式事务删除

    java代码:
    1. <aop:config expose-proxy="true">  
    2.         <!-- 只对业务逻辑层实施事务 -->  
    3.         <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />  
    4.         <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>  
    5.     </aop:config>  

        并添加注解式事务支持:

    java代码:
    1. <tx:annotation-driven transaction-manager="txManager"/>  

        在我们的BaseService接口上添加 @Transactional 使该方法开启事务

    java代码:
    1. package cn.javass.common.service;  
    2. public interface IBaseService<M extends java.io.Serializable, PK extends java.io.Serializable> {  
    3. @Transactional   //开启默认事务  
    4.     public int countAll();  
    5. }    

    在我们的log4j.properties中添加如下配置,表示输出spring的所有debug信息

    java代码:
    1. log4j.logger.org.springframework=INFO,CONSOLE  

    在我们的resources.properties里将hibernate.show_sql=true 改为true,为了看到hibernate的sql。

    单元测试类:

    java代码:
    1. package cn.javass.ssonline.spider.service.impl;  
    2.   
    3. import org.junit.Test;  
    4. import org.junit.runner.RunWith;  
    5. import org.springframework.beans.factory.annotation.Autowired;  
    6. import org.springframework.test.context.ContextConfiguration;  
    7. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
    8. import org.springframework.test.context.transaction.TransactionConfiguration;  
    9.   
    10. import cn.javass.demo.service.UserService;  
    11. @RunWith(SpringJUnit4ClassRunner.class)  
    12. @ContextConfiguration(locations = {"classpath:spring-config.xml"})  
    13. public class UserServiceTest2 {  
    14.       
    15.     @Autowired  
    16.     private UserService userService;  
    17.     @Test  
    18.     public void testCreate() {  
    19.        userService.countAll();  
    20.     }  
    21. }  

    基础工作做好,接下来我们详细看看 Spring基于 JDK动态代理 和 CGLIB类级别代理到底有什么区别。

    二、基于JDK动态代理:

    java代码:
    1. <tx:annotation-driven transaction-manager="txManager"/>  

       该配置方式默认就是JDK动态代理方式

    运行单元测试,核心日志如下:                                     

    java代码:
    1. 2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [cn.javass.common.service.impl.BaseService.countAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''      //开启事务  
    2. 2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session   
    3.   
    4. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.hibernate4.SessionHolder@1184a4ffor key [org.hibernate.internal.SessionFactoryImpl@107b56e] to thread [main] //绑定session到ThreadLocal  
    5. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization  
    6. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]  
    7. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4ffor key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]  
    8. 2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session   
    9.   
    10. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4ffor key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]  
    11. Hibernate:   
    12.     select  
    13.         count(*) as col_0_0_   
    14.     from  
    15.         tbl_user usermodel0_  
    16.   
    17. 2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session    //提交事务  
    18.   
    19. 2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Removed value [org.springframework.orm.hibernate4.SessionHolder@1184a4ffor key [org.hibernate.internal.SessionFactoryImpl@107b56e] from thread [main] //解除绑定session到ThreadLocal  

    到此我们可以看到事务起作用了,也就是说即使把@Transactional放到接口上 基于JDK动态代理也是可以工作的。

    三、基于CGLIB类代理:

    java代码:
    1. <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>  

       该配置方式是基于CGLIB类代理

    启动测试会报错,No Session found for current thread,说明事务没有起作用

     

    java代码:
    1. org.hibernate.HibernateException: No Session found for current thread  
    2.     at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)  
    3.     at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1024)  
    4.     at cn.javass.common.dao.hibernate4.BaseHibernateDao.getSession(BaseHibernateDao.java:63)  
    5.     at cn.javass.common.dao.hibernate4.BaseHibernateDao.aggregate(BaseHibernateDao.java:238)  
    6.     at cn.javass.common.dao.hibernate4.BaseHibernateDao.countAll(BaseHibernateDao.java:114)  
    7.     at cn.javass.common.service.impl.BaseService.countAll(BaseService.java:60)  
    8.     at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(<generated>)  
    9.     at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)  
    10.     at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:618)  
    11.     at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(<generated>)  
    12.     at cn.javass.ssonline.spider.service.impl.UserServiceTest2.testCreate(UserServiceTest2.java:20)  

     

     

    如果将注解放在具体类上或具体类的实现方法上才会起作用。

    java代码:
    1. package cn.javass.common.service.impl;  
    2. public abstract class BaseService<M extends java.io.Serializable, PK extends java.io.Serializable> implements IBaseService<M, PK> {  
    3.    
    4.    @Transactional()   //放在抽象类上  
    5.     @Override  
    6.     public int countAll() {  
    7.         return baseDao.countAll();  
    8.     }  
    9. }  

    运行测试类,将发现成功了,因为我们的UserService继承该方法,但如果UserService覆盖该方法,如下所示,也将无法织入事务(报错):

    java代码:
    1. package cn.javass.demo.service.impl;  
    2. public class UserServiceImpl extends BaseService<UserModel, Integer> implements UserService {  
    3.     //没有@Transactional  
    4.     @Override   
    5.     public int countAll() {  
    6.         return baseDao.countAll();  
    7.     }  
    8. }  


    四、基于aspectj的

    java代码:
    1. <tx:annotation-driven transaction-manager="txManager" mode="aspectj" proxy-target-class="true"/>  

    在此就不演示了,我们主要分析基于JDK动态代理和CGLIB类代理两种的区别。

    五、结论:

    基于JDK动态代理 ,可以将@Transactional放置在接口和具体类上。

    基于CGLIB类代理,只能将@Transactional放置在具体类上。

    因此 在实际开发时全部将@Transactional放到具体类上,而不是接口上。

    六、分析

    1、  JDK动态代理

    1.1、Spring使用JdkDynamicAopProxy实现代理:

    java代码:
    1. package org.springframework.aop.framework;  
    2. final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {  
    3.     //注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)  
    4.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    5.     }  
    6. }  

    注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)

    1.2、如果<tx:annotation-driven 中 proxy-target-class="true" ,Spring将使用CGLIB动态代理,而内部通过Cglib2AopProxy实现代理,而内部通过DynamicAdvisedInterceptor进行拦截:

    java代码:
    1. package org.springframework.aop.framework;  
    2. final class Cglib2AopProxy implements AopProxy, Serializable {  
    3.     private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {  
    4.             //注意此处的method 一定是具体类上的method(因此只用放置在具体类上的@Transactional是可以发现的)  
    5.         public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
    6.             }  
    7.        }  
    8. }  

    1.3、Spring使用AnnotationTransactionAttributeSource通过查找一个类或方法是否有@Transactional注解事务来返回TransactionAttribute(表示开启事务):

    java代码:
    1. package org.springframework.transaction.annotation;  
    2. public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {  
    3.          protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {  
    4.         for (TransactionAnnotationParser annotationParser : this.annotationParsers) {  
    5.             TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);  
    6.             if (attr != null) {  
    7.                 return attr;  
    8.             }  
    9.         }  
    10.         return null;  
    11.     }  
    12. }  

    而AnnotationTransactionAttributeSource又使用SpringTransactionAnnotationParser来解析是否有@Transactional注解:

    java代码:
    1. package org.springframework.transaction.annotation;  
    2.   
    3. public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {  
    4.   
    5.     public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {  
    6.         Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);  
    7.         if (ann != null) {  
    8.             return parseTransactionAnnotation(ann);  
    9.         }  
    10.         else {  
    11.             return null;  
    12.         }  
    13.     }  
    14.   
    15.     public TransactionAttribute parseTransactionAnnotation(Transactional ann) {  
    16.           
    17.     }  
    18.   
    19. }  

        此处使用AnnotationUtils.getAnnotation(ae, Transactional.class); 这个方法只能发现当前方法/类上的注解,不能发现父类的注解。 Spring还提供了一个 AnnotationUtils.findAnnotation()方法 可以发现父类/父接口中的注解(但spring没有使用该接口)。

       如果Spring此处换成AnnotationUtils.findAnnotation(),将可以发现父类/父接口中的注解。

    这里还一个问题,描述如下:

     

    在接口中删除@Transactional   //开启默认事务

    java代码:
    1. package cn.javass.common.service;  
    2. public interface IBaseService<M extends java.io.Serializable, PK extends java.io.Serializable> {  
    3.     public int countAll();  
    4. }    

    在具体类中添加@Transactional

    java代码:
    1. package cn.javass.common.service.impl;  
    2. public abstract class BaseService<M extends java.io.Serializable, PK extends java.io.Serializable> implements IBaseService<M, PK> {  
    3.   
    4.     @Transactional()   //开启默认事务  
    5.     @Override  
    6.     public int countAll() {  
    7.         return baseDao.countAll();  
    8.     }  
    9. }  

     

     

     

    问题:

        我们之前说过,基于JDK动态代理时, method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的),但现在我们放在具体类上,那么Spring是如何发现的呢??

        还记得发现TransactionAttribute是通过AnnotationTransactionAttributeSource吗?具体看步骤1.3:

    而AnnotationTransactionAttributeSource 继承AbstractFallbackTransactionAttributeSource

    java代码:
    1. package org.springframework.transaction.interceptor;  
    2. public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {  
    3.   
    4.     public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {  
    5.         //第一次 会委托给computeTransactionAttribute  
    6. }  
    7.   
    8.     //计算TransactionAttribute的  
    9.     private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {  
    10.           
    11.         //省略  
    12.   
    13.         // Ignore CGLIB subclasses - introspect the actual user class.  
    14.         Class<?> userClass = ClassUtils.getUserClass(targetClass);  
    15.         // The method may be on an interface, but we need attributes from the target class.  
    16.         // If the target class is null, the method will be unchanged.  
    17.         //①此处将查找当前类覆盖的方法  
    18.         Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);  
    19.         // If we are dealing with method with generic parameters, find the original method.  
    20.         specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);  
    21.   
    22.         // First try is the method in the target class.  
    23.         TransactionAttribute txAtt = findTransactionAttribute(specificMethod);  
    24.         if (txAtt != null) {  
    25.             return txAtt;  
    26.         }  
    27.   
    28.         //找类上边的注解  
    29.         // Second try is the transaction attribute on the target class.  
    30.         txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());  
    31.         if (txAtt != null) {  
    32.             return txAtt;  
    33.         }  
    34.         //②如果子类覆盖的方法没有 再直接找当前传过来的  
    35.         if (specificMethod != method) {  
    36.             // Fallback is to look at the original method.  
    37.             txAtt = findTransactionAttribute(method);  
    38.             if (txAtt != null) {  
    39.                 return txAtt;  
    40.             }  
    41.             // Last fallback is the class of the original method.  
    42.             return findTransactionAttribute(method.getDeclaringClass());  
    43.         }  
    44.         return null;  
    45.     }  
    46. }  

     

           //①此处将查找子类覆盖的方法

           Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);

     

            // ClassUtils.getMostSpecificMethod

           public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {

           Method specificMethod = null;

           if (method != null && isOverridable(method, targetClass) &&

                  targetClass != null && !targetClass.equals(method.getDeclaringClass())) {

               try {

                  specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());

               } catch (AccessControlException ex) {

                  // security settings are disallowing reflective access; leave

                  // 'specificMethod' null and fall back to 'method' below

               }

           }

           return (specificMethod != null ? specificMethod : method);

        }

     

        可以看出将找到当前类的那个方法。因此我们放置在BaseService countAll方法上的@Transactional起作用了。

     

     

          //②如果子类覆盖的方法没有 再直接找当前传过来的

           if (specificMethod != method) {

               // Fallback is to look at the original method.

               txAtt = findTransactionAttribute(method);

               if (txAtt != null) {

                  return txAtt;

               }

               // Last fallback is the class of the original method.

               return findTransactionAttribute(method.getDeclaringClass());

           }

     

           查找子类失败时直接使用传过来的方法。

     

     

     

     

    因此,建议大家使用基于Schema风格的事务(不用考虑这么多问题,也不用考虑是类还是方法)。而@Transactional建议放置到具体类上,不要放置到接口。


    原创地址:http://sishuok.com/forum/blogPost/list/0/3845.html#9317

  • 相关阅读:
    php原生PHPExcel插件导表(附表格合并,加粗居中及加边框换行操作)
    VMware虚拟机基于contos 7 搭建lnmp环境全过程
    菜鸟学git的基本命令及常见错误
    为什么要写blog????
    JavaScript高级程序设计--函数小记
    jsapi微信支付
    Javascript模式小记(一)
    图片的增删查
    图形的滚动及颜色区域的拉移
    网页中的宽与高
  • 原文地址:https://www.cnblogs.com/wyang0126/p/5039907.html
Copyright © 2011-2022 走看看