`
猫不吃的鱼
  • 浏览: 157489 次
  • 性别: Icon_minigender_1
  • 来自: 芜湖市
社区版块
存档分类
最新评论

(转)JDK代理 CGLIB代理的实现Spring注解管理事务区别。

阅读更多
转自 http://blog.csdn.net/zhujyy110/article/details/8639119

JDK代理 CGLIB代理的实现Spring注解管理事务区别。
.


javaspring jdk动态代理CGLIB代理动态代理


一、基础工作
首先修改我们上一次做的 SpringMVC + spring3.1.1 + hibernate4.1.0 http://www.2cto.com/kf/201203/122443.html集成及常见问题总结,如下所示:
     将xml声明式事务删除





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



    并添加注解式事务支持:





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

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





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



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



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

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





单元测试类:



java代码:
package cn.javass.ssonline.spider.service.impl;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

import cn.javass.demo.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class UserServiceTest2 {
   
     @Autowired
     private UserService userService;
     @Test
     public void testCreate() {
        userService.countAll();
     }
}



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





二、基于JDK动态代理:
 

java代码:
<tx:annotation-driven transaction-manager="txManager"/>
    该配置方式默认就是JDK动态代理方式



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



java代码:
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; ''      //开启事务
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session

2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] to thread [main] //绑定session到ThreadLocal
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session

2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
Hibernate:
     select
         count(*) as col_0_0_
     from
         tbl_user usermodel0_

2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session    //提交事务

2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Removed value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] from thread [main] //解除绑定session到ThreadLocal



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





三、基于CGLIB类代理:
 

java代码:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
    该配置方式是基于CGLIB类代理



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



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





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



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



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



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






四、基于aspectj的



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

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



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



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



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



java代码:
package org.springframework.transaction.annotation;
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
          protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
   for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
    TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
    if (attr != null) {
     return attr;
    }
   }
   return null;
  }
}

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





java代码:
package org.springframework.transaction.annotation;

public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
   Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
   if (ann != null) {
    return parseTransactionAnnotation(ann);
   }
   else {
    return null;
   }
  }

public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
  
     }

}



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



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









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



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





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



在具体类中添加@Transactional





java代码:
package cn.javass.common.service.impl;
public abstract class BaseService<M extends java.io.Serializable, PK extends java.io.Serializable> implements IBaseService<M, PK> {

    @Transactional()   //开启默认事务
     @Override
     public int countAll() {
         return baseDao.countAll();
     }
}


问题:

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

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



而AnnotationTransactionAttributeSource 继承AbstractFallbackTransactionAttributeSource



java代码:
package org.springframework.transaction.interceptor;
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
   //第一次 会委托给computeTransactionAttribute
}

    //计算TransactionAttribute的
  private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
  
   //省略

  // Ignore CGLIB subclasses - introspect the actual user class.
   Class<?> userClass = ClassUtils.getUserClass(targetClass);
   // The method may be on an interface, but we need attributes from the target class.
   // If the target class is null, the method will be unchanged.
   //①此处将查找当前类覆盖的方法
   Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
   // If we are dealing with method with generic parameters, find the original method.
   specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

  // First try is the method in the target class.
   TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
   if (txAtt != null) {
    return txAtt;
   }

  //找类上边的注解
   // Second try is the transaction attribute on the target class.
   txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
   if (txAtt != null) {
    return txAtt;
   }
   //②如果子类覆盖的方法没有 再直接找当前传过来的
   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());
   }
   return null;
  }
}





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

       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建议放置到具体类上,不要放置到接口。
分享到:
评论

相关推荐

    spring杂谈 作者zhang KaiTao

    1.10 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。 1.11 在spring中获取代理对象代理的目标对象工具类 1.12 如何为spring代理类设置属性值 1.13 我对SpringDAO层支持...

    spring2.5学习PPT 传智博客

    使用Spring注解方式管理事务与传播行为详解 24.使用Spring配置文件实现事务管理 25.搭建和配置Spring与Hibernate整合的环境 26.Spring集成的Hibernate编码与测试 27.Struts与Spring集成方案1(Struts集成Spring) ...

    Spring3注解

    AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于...而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。

    spring.doc

    5.1.8.1Spring的事务管理器 117 5.1.8.2Spring事务的传播属性 117 5.1.8.3Spring事务的隔离级别 117 拓展: 118 5.1.8.4以XML配置的 形式 119 拓展: 120 5.1.8.5以注解方式配置 125 拓展: 127 5.1.9使用CGLIB以XML...

    SpringBoot下的SpringAOP-day04-源代码

    SpringBoot下的Spring——DAY04——动态代理总结、AOP、自定义注解进行拦截、动态获取注解参数、通知方法 1.动态代理总结 1.1 JDK动态代理特点 1.2 CGlib动态代理 1.2.1 CGLib特点说明 1.3 动态代理的作用 2 Spring...

    Jdk动态代理 底层

    前言 java动态代理主要有2种,Jdk动态代理、Cglib动态...需要注意的是,Jdk动态代理相比起cglib动态代理,Jdk动态代理的对象必须实现接口,否则将报错。我们也将带着这个问题在源码分析中寻找答案 当@Cacheable注解在方

    Spring AOP源码深度解析:掌握Java高级编程核心技术

    动态代理是实现AOP的基础,它通过JDK动态代理或CGLIB代理生成被代理对象的子类。通知是织入到目标对象连接点上的一段程序,例如@Before、@After等。 切点定义了通知应该在哪些连接点上触发。而切面则是通知和切点的...

    Spring.html

    JDK动态代理(默认) 基于接口:代理对象与目标对象是兄弟关系,目标类必须实现接口 CGLIB动态代理 基于父类:代理对象与目标对象是父子关系.目标不能被final修饰 修改默认代理方法: 增强种类 前置通知 ...

    Spring.3.x企业应用开发实战(完整版).part2

    9.3.2 Spring的事务管理器实现类 9.3.3 事务同步管理器 9.3.4 事务传播行为 9.4 编程式的事务管理 9.5 使用XML配置声明式事务 9.5.1 一个将被实施事务增强的服务接口 9.5.2 使用原始的 TransactionProxyFactoryBean ...

    Spring中文帮助文档

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”通知器 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    Spring API

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”通知器 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    Spring 2.0 开发参考手册

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动代理...

    spring chm文档

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    Spring3.x企业应用开发实战(完整版) part1

    9.3.2 Spring的事务管理器实现类 9.3.3 事务同步管理器 9.3.4 事务传播行为 9.4 编程式的事务管理 9.5 使用XML配置声明式事务 9.5.1 一个将被实施事务增强的服务接口 9.5.2 使用原始的 TransactionProxyFactoryBean ...

    Spring AOP与动态代理

    AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或...Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的

    java-advance:java进阶,包含 常用设计模式、线程和并发、spring核心代码、mybatis核心代码、springboot2.0、springcloud G版本、docker的使用、各类面试题

    java进阶源码分析专题常用设计模式线程与并发锁的使用深度理解synchronized、volatile、cas手写ASQSpring5IOC容器设计原理及高级特性AOP设计原理FactoryBean与BeanFactorySpring事务处理机制Spring JDK动态代理...

    AOP编程示例

    CGlib的Proxy实现AOP的示例、JDK的Proxy实现AOP的示例、通过spring注解方式实现AOP

    AOP 切面编程的两种方式xml 和注解

    Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目...

Global site tag (gtag.js) - Google Analytics