基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。
一、基础工作
将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]
- 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]
到此我们可以看到事务起作用了,也就是说即使把@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 {
-
- @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 {
-
- 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 {
-
- 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) {
-
- }
-
-
- private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
-
-
-
-
- Class<?> userClass = ClassUtils.getUserClass(targetClass);
-
-
-
- Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
-
- specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
-
-
- TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
- if (txAtt != null) {
- return txAtt;
- }
-
-
-
- txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
- if (txAtt != null) {
- return txAtt;
- }
-
- if (specificMethod != method) {
-
- txAtt = findTransactionAttribute(method);
- if (txAtt != null) {
- return txAtt;
- }
-
- 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框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...
Java 动态代理详解(代理模式+静态代理+JDK动态代理+CGLIB动态代理) Java 动态代理是 Java 编程语言中的一种强大工具,广泛应用于 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、Java 注解...
首先,JDK动态代理基于Java的反射机制,通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现。Proxy类用于创建一个代理对象,而InvocationHandler接口定义了一个方法`invoke()`,该...
5. **配置代理**:Spring会根据目标对象是否实现了接口来决定使用JDK动态代理还是CGLIB代理。如果目标对象实现了接口,Spring会选择JDK动态代理。动态代理类会继承自`java.lang.reflect.Proxy`,并实现目标对象的...
本资料“动态代理和注解Spring.rar”着重介绍了Spring框架中的两个关键概念:动态代理和注解的使用。 首先,我们来深入了解Spring中的动态代理。动态代理在Spring AOP(面向切面编程)中扮演着重要角色。AOP允许...
- Spring支持两种类型的代理:JDK动态代理(如果目标对象实现了接口)和CGLIB代理(如果目标对象没有接口,使用字节码生成技术)。 - 在Spring配置中,可以使用`@Aspect`注解定义切面,`@Before`、`@After`、`@...
在Spring中,AOP主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...
Spring框架提供了一种实现AOP的方式,它支持基于代理的AOP实现,包括JDK动态代理和CGLIB代理。当目标对象不实现接口时,Spring会使用CGLIB来创建代理对象。 CGLIB(Code Generation Library)是一个代码生成库,它...
Spring框架提供了两种动态代理机制:JDK动态代理和CGLIB代理,它们分别基于这两种技术。 1. JDK动态代理: - 当被代理的目标对象实现了至少一个接口时,Spring会选择使用JDK动态代理。它通过反射机制生成一个实现...
Spring的AOP实现有两种方式:JDK动态代理和CGLIB字节码代理。当目标对象实现了一个或多个接口时,Spring会选择使用JDK动态代理。以下是Spring AOP使用JDK动态代理的基本步骤: 1. **创建代理对象**:Spring会创建一...
- 如果在AOP代理模式下使用(如JDK动态代理或CGLIB代理),注解应该应用在接口方法或实现类的方法上,而不是两者的混合使用。 - 事务管理需要在Spring配置中启用,例如在xml配置中添加 `...
在Java开发中,JDK动态代理是一个非常重要的概念,它允许我们在运行时创建对已有接口的代理实现。Spring框架在很多场景下也利用了这一特性,例如AOP(面向切面编程)就是其中之一。"spring-demo16-JDK动态代理.zip...
在Spring中,动态代理主要有两种实现方式:JDK动态代理和CGLIB代理。它们都是为了实现AOP(面向切面编程)的核心技术。下面将详细介绍这两种动态代理机制。 1. JDK动态代理: JDK动态代理基于Java的接口实现。如果...
- **JDK动态代理**:基于接口实现,如果目标对象实现了至少一个接口,Spring会选择使用JDK动态代理。代理对象会实现与目标对象相同的接口,并在调用接口方法时执行拦截逻辑。 - **CGLIB代理**:如果目标对象没有...
Spring AOP支持不同的代理策略,包括JDK动态代理和CGLIB代理。如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中...
代理可以是JDK动态代理或CGLIB代理,取决于目标bean是否实现了接口。 事务管理中的`@Transactional`注解则通过TransactionAttributeSource和TransactionInterceptor协同工作。当事务方法被调用时,拦截器会检查方法...
在本场景下,"springboot中的 动态代理的选择测试代码"涉及到的是如何在Spring Boot环境中利用Java或CGLIB动态代理来实现方法拦截、事务管理、日志记录等跨切面关注点。下面我们将深入探讨这一主题。 首先,让我们...
在Spring中,AOP主要是通过代理模式实现的,有JDK动态代理和CGLIB两种方式。 对于Spring事务管理,我们通常有两种方式:编程式和声明式。编程式事务管理需要在代码中显式地调用begin、commit、rollback等事务控制...
在Spring中,AOP代理有两种实现方式:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的类,而CGLIB代理则适用于未实现接口的类。 1. **JDK动态代理**: - Spring通过实现`java.lang.reflect.InvocationHandler`...
JDK动态代理基于接口实现,它通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来创建代理对象。首先,我们需要定义一个或多个接口,然后创建实现这些接口的目标对象。接着,我们实现`...