基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。
一、基础工作
首先修改我们上一次做的 SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结,如下所示:
将xml声明式事务删除
- <aop:config expose-proxy="true">
- <!-- 只对业务逻辑层实施事务 -->
- <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />
- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
- </aop:config>
并添加注解式事务支持:
- <tx:annotation-driven transaction-manager="txManager"/>
在我们的BaseService接口上添加 @Transactional 使该方法开启事务
- 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信息
- log4j.logger.org.springframework=INFO,CONSOLE
在我们的resources.properties里将hibernate.show_sql=true 改为true,为了看到hibernate的sql。
单元测试类:
- 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动态代理:
- <tx:annotation-driven transaction-manager="txManager"/>
该配置方式默认就是JDK动态代理方式
运行单元测试,核心日志如下:
- -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; '' //开启事务
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
- -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
- -03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
- -03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
- -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]
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
- -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_
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session //提交事务
- -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类代理:
- <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
该配置方式是基于CGLIB类代理
启动测试会报错,No Session found for current thread,说明事务没有起作用
- org.hibernate.HibernateException: No Session found for current thread
- )
- )
- )
- )
- )
- )
- at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(<generated>)
- )
- )
- at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(<generated>)
- )
如果将注解放在具体类上或具体类的实现方法上才会起作用。
- 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覆盖该方法,如下所示,也将无法织入事务(报错):
- package cn.javass.demo.service.impl;
- public class UserServiceImpl extends BaseService<UserModel, Integer> implements UserService {
- //没有@Transactional
- @Override
- public int countAll() {
- return baseDao.countAll();
- }
- }
四、基于aspectj的
- <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实现代理:
- 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进行拦截:
- 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(表示开启事务):
- 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注解:
- 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 //开启默认事务
- package cn.javass.common.service;
- public interface IBaseService<M extends java.io.Serializable, PK extends java.io.Serializable> {
- public int countAll();
- }
在具体类中添加@Transactional
- 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
- 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建议放置到具体类上,不要放置到接口。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。
一、基础工作
首先修改我们上一次做的 SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结,如下所示:
将xml声明式事务删除
- <aop:config expose-proxy="true">
- <!-- 只对业务逻辑层实施事务 -->
- <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />
- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
- </aop:config>
并添加注解式事务支持:
- <tx:annotation-driven transaction-manager="txManager"/>
在我们的BaseService接口上添加 @Transactional 使该方法开启事务
- 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信息
- log4j.logger.org.springframework=INFO,CONSOLE
在我们的resources.properties里将hibernate.show_sql=true 改为true,为了看到hibernate的sql。
单元测试类:
- 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动态代理:
- <tx:annotation-driven transaction-manager="txManager"/>
该配置方式默认就是JDK动态代理方式
运行单元测试,核心日志如下:
- -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; '' //开启事务
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
- -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
- -03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
- -03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
- -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]
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
- -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_
- -03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session //提交事务
- -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类代理:
- <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
该配置方式是基于CGLIB类代理
启动测试会报错,No Session found for current thread,说明事务没有起作用
- org.hibernate.HibernateException: No Session found for current thread
- )
- )
- )
- )
- )
- )
- at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(<generated>)
- )
- )
- at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(<generated>)
- )
如果将注解放在具体类上或具体类的实现方法上才会起作用。
- 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覆盖该方法,如下所示,也将无法织入事务(报错):
- package cn.javass.demo.service.impl;
- public class UserServiceImpl extends BaseService<UserModel, Integer> implements UserService {
- //没有@Transactional
- @Override
- public int countAll() {
- return baseDao.countAll();
- }
- }
四、基于aspectj的
- <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实现代理:
- 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进行拦截:
- 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(表示开启事务):
- 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注解:
- 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 //开启默认事务
- package cn.javass.common.service;
- public interface IBaseService<M extends java.io.Serializable, PK extends java.io.Serializable> {
- public int countAll();
- }
在具体类中添加@Transactional
- 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
- 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建议放置到具体类上,不要放置到接口。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。的更多相关文章
- Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。
借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...
- Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
- JDK动态代理和CGLib动态代理简单演示
JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...
- jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)
代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...
- 代理模式之静态代理,JDK动态代理和cglib动态代理
代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...
- jdk动态代理和cglib动态代理的区别
一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...
- jdk 动态代理和 cglib 动态代理
原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载 ...
- 动态代理:jdk动态代理和cglib动态代理
/** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...
随机推荐
- 解决Scrollview 嵌套recyclerview不能显示,高度不正常的问题
我们先看一个效果,问题说的就是中间的Grid效果在Scrollview 嵌套recyclerview显示问题,在Android Api 24是好的,不过在5,1,1版本(api 22)缺出现了问题 最 ...
- EBS的性能调优
metalink Tuning performance on eBusiness suite (Doc ID 744143.1) 这篇文档描述了如何调查电子商务套件的整体性能下降. ...
- golang函数可变参数传递性能问题
几天前纠结了一个蛋疼的问题,在go里面函数式支持可变参数的,譬如...T,go会创建一个slice,用来存放传入的可变参数,那么,如果创建一个slice,例如a,然后以a...这种方式传入,go会不会 ...
- struts2国际化全例 错误解决
在struts2中需要做国际化的有: jsp页面的国际化,action错误信息的国际化,转换错误信息的国际化,校验错误信息的国际化 在之前的例子中已经做过和国际化相关的例子了,在struts.xml中 ...
- Cocos2D将v1.0的tileMap游戏转换到v3.4中一例(一)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 首先说一下为什么要转换,这是为了后面的A*寻路算法做准备.由于在 ...
- Linux中的端口占用问题
本文将会阐述两种解决端口占用的方法. 本文会用到的服务器端的程序如下: #include "unp.h" #include <time.h> int main(int ...
- Unity2D Sprite灰白图(Unity3D开发之十一)
猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=596 昨晚看到群里问到2DSpr ...
- Android-获取全局Context的技巧-android学习之旅(68)
我们经常需要获取全局的Context ,比如弹出Toast,启动活动,服务,接收器,还有自定义控件,操作数据库,使用通知等 通常的方法是在调用的地方传入Context参数 ,有时候这种不会奏效,教给大 ...
- 【Django】优化小技巧之清除过期session
事情是这样的,大概也就几万注册用户的站点(使用django1.6), session 存储在关系型数据库,这次上线之后发现session表几十万数据了,过期session没有被自动删除 思考 官网 s ...
- wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果
上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个 两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...