`

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

 
阅读更多

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

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

一、基础工作

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

将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

publicstaticMethod 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) {

returntxAtt;

}

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

returnfindTransactionAttribute(method.getDeclaringClass());

}

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

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

作者原创【http://sishuok.com/forum/blogPost/list/0/3845.html#9317

分享到:
评论

相关推荐

    spring杂谈 作者zhang KaiTao

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

    Jdk动态代理 底层

    java动态代理主要有2种,Jdk动态代理、Cglib动态代理,本文主要讲解Jdk动态代理的使用、运行机制、以及源码分析。当spring没有手动开启Cglib动态代理,即:或@EnableAspectJAutoProxy(proxyTargetClass = true),...

    Spring3注解

    AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表;而动态代理则以 Spring AOP 为代表。其中静态代理是指使用 AOP 框架提供的命令进行编译...

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

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

    SpringBoot下的SpringAOP-day04-源代码

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

    Spring AOP与动态代理

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

    spring.doc

    4.1.3 Spring的动态代理 71 4.2 AOP编程 71 4.2.1概念: 71 SpringAOP概念拓展: 73 之前实现了目标方法的动态调用,现在来实现切面的动态调用。 74 4.2.2 AOP实现的两种模式 78 4.2.2.1 xml形式 78 XML形式拓展: ...

    spring2.5学习PPT 传智博客

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

    41 注解 @Aspect 是如何工作的?慕课专栏(1)1

    背景面试官:如何针对某个包下的所有类的方法打印日志?面试者:可以使用 Spring AOP 的 @Aspect 注解实现,底层由 JDK 动态代理和 CGLib

    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中文帮助文档

    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.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.html

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

    Spring 2.0 开发参考手册

    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. 使用“自动...

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

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

    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 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. 使用“自动...

    spring5系列 任小龙大神班

    STS工具、IoC、DI思想、Spring测试框架、bean的实例化、作用域、初始化、生命周期、静态代理、字节码加载原理、JDK动态代理和原理、CGLIB动态代理和原理、拦截器思想、AOP思想和开发、Spring对持久层技术支持、...

Global site tag (gtag.js) - Google Analytics