基于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. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.test.context.ContextConfiguration;
  6. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  7. import org.springframework.test.context.transaction.TransactionConfiguration;
  8. import cn.javass.demo.service.UserService;
  9. @RunWith(SpringJUnit4ClassRunner.class)
  10. @ContextConfiguration(locations = {"classpath:spring-config.xml"})
  11. public class UserServiceTest2 {
  12. @Autowired
  13. private UserService userService;
  14. @Test
  15. public void testCreate() {
  16. userService.countAll();
  17. }
  18. }

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

二、基于JDK动态代理:

java代码:

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

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

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

java代码:

  1. -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. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
  3. -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
  4. -03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
  5. -03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
  6. -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]
  7. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
  8. -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]
  9. Hibernate:
  10. select
  11. count(*) as col_0_0_
  12. from
  13. tbl_user usermodel0_
  14. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session    //提交事务
  15. -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代码:

  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. )
  3. )
  4. )
  5. )
  6. )
  7. )
  8. at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(<generated>)
  9. )
  10. )
  11. at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(<generated>)
  12. )

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

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. @Transactional()   //放在抽象类上
  4. @Override
  5. public int countAll() {
  6. return baseDao.countAll();
  7. }
  8. }

运行测试类,将发现成功了,因为我们的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. public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
  3. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
  4. Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
  5. if (ann != null) {
  6. return parseTransactionAnnotation(ann);
  7. }
  8. else {
  9. return null;
  10. }
  11. }
  12. public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
  13. }
  14. }

此处使用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. @Transactional()   //开启默认事务
  4. @Override
  5. public int countAll() {
  6. return baseDao.countAll();
  7. }
  8. }

问题:

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

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

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

基于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. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.test.context.ContextConfiguration;
  6. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  7. import org.springframework.test.context.transaction.TransactionConfiguration;
  8. import cn.javass.demo.service.UserService;
  9. @RunWith(SpringJUnit4ClassRunner.class)
  10. @ContextConfiguration(locations = {"classpath:spring-config.xml"})
  11. public class UserServiceTest2 {
  12. @Autowired
  13. private UserService userService;
  14. @Test
  15. public void testCreate() {
  16. userService.countAll();
  17. }
  18. }

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

二、基于JDK动态代理:

java代码:

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

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

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

java代码:

  1. -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. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
  3. -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
  4. -03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
  5. -03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
  6. -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]
  7. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
  8. -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]
  9. Hibernate:
  10. select
  11. count(*) as col_0_0_
  12. from
  13. tbl_user usermodel0_
  14. -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session    //提交事务
  15. -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代码:

  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. )
  3. )
  4. )
  5. )
  6. )
  7. )
  8. at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(<generated>)
  9. )
  10. )
  11. at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(<generated>)
  12. )

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

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. @Transactional()   //放在抽象类上
  4. @Override
  5. public int countAll() {
  6. return baseDao.countAll();
  7. }
  8. }

运行测试类,将发现成功了,因为我们的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. public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
  3. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
  4. Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
  5. if (ann != null) {
  6. return parseTransactionAnnotation(ann);
  7. }
  8. else {
  9. return null;
  10. }
  11. }
  12. public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
  13. }
  14. }

此处使用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. @Transactional()   //开启默认事务
  4. @Override
  5. public int countAll() {
  6. return baseDao.countAll();
  7. }
  8. }

问题:

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

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

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

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

  1. Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。

    借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...

  2. Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  3. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  4. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  5. jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

    代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...

  6. 代理模式之静态代理,JDK动态代理和cglib动态代理

    代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...

  7. jdk动态代理和cglib动态代理的区别

    一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...

  8. jdk 动态代理和 cglib 动态代理

    原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载 ...

  9. 动态代理:jdk动态代理和cglib动态代理

    /** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...

随机推荐

  1. HDU2612---(两次BFS)

    Description Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. Leave Nin ...

  2. centos7安装jdk,tomcat,msyql(MariaDB)

    操作系统版本 CentOS Linux release 7.2.1511 (Core) 安装jdk 下载jdk-8u66-linux-x64.rpm上传到linux上 先改用户权限 然后 rpm -i ...

  3. [sersync+rsync] centos6.5 远程文件同步部署记录

    针对本地文件的修改,自动同步到远程文件夹,远程备份很方面.研究了下大家的主流同步方案一般是 rsync+inotify和rsync+sersync, 我这里使用sersync的方案,当然大部分都是参照 ...

  4. React Native之Navigator

    移动应用很少只包含一个页面.从你添加第二个页面开始,就得考虑如何管理多个页面间的跳转了. 导航器正是为此而生.它可以管理多个页面间的跳转,也包含了一些常见的过渡动画,包括水平翻页.垂直弹出等等. Na ...

  5. Maya人物骨骼创建与蒙皮

    参考: HumanIK骨架的使用与蒙皮的操作 注意事项: 1. 编辑好骨骼一侧后,可删除另一侧,并使用镜像操作.镜像操作可以指定替换生成骨骼的名字中的子字符串. 2. 如果在编辑骨骼的时候由删除添加过 ...

  6. LCD正向扫描和反向扫描

    LCD正向扫描和反向扫描 LCD扫描一般分正向扫面和反向扫描,分别针对正装和倒装结构(如下): 有时候提到长边扫描和短边扫描应该是针对横屏和竖屏的设置,大部分显示屏是正向扫描,是否都支持,和玻璃有关, ...

  7. 【Swift学习笔记00】——enumeration枚举类型遵循协议protocol

    Apple官方文档:The Swift Programming LanguageProtocols and Extensions一节的小节练习,要求自行定义一个enumeration枚举类型,并且遵循 ...

  8. 海量数据挖掘MMDS week4: 推荐系统之数据降维Dimensionality Reduction

    http://blog.csdn.net/pipisorry/article/details/49231919 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  9. 客户地点分配多OU

    DECLARE l_num_user_id NUMBER; l_num_appl_id NUMBER; l_num_resp_id NUMBER; cust_account_rec_type hz_c ...

  10. Gradle实现的两种简单的多渠道打包方法

    本来计划今天发Android的官方技术文档的翻译--<Gradle插件用户指南>的第五章的,不过由于昨天晚上没译完,还差几段落,所以只好推后了. 今天就说一下使用Gradle进行类似友盟这 ...