查看原文
其他

【023期】Spring 中 @Transactional 是如何实现的?源码解读原理

Java精选 2022-08-09

>>号外:关注“Java精选”公众号,回复“2021面试题”,领取免费资料!Java精选面试题”小程序,3000+ 道面试题在线刷,最新、最全 Java 面试题!

@Transactional注解简介
@Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚。@Transactional注解可以帮助我们把事务开启、提交或者回滚的操作,通过aop的方式进行管理。通过@Transactional注解就能让spring为我们管理事务,免去了重复的事务管理逻辑,减少对业务代码的侵入,使我们开发人员能够专注于业务层面开发。
我们知道实现@Transactional原理是基于spring aop,aop又是动态代理模式的实现,通过对源码的阅读,总结出下面的步骤来了解实际中,在spring 是如何利用aop来实现@Transactional的功能的。如果对spring的aop实现原理不了解,可以看aop实现原理分析。
spring中声明式事务实现原理猜想
1、首先,对于spring中aop实现原理有了解的话,应该知道想要对一个方法进行代理的话,肯定需要定义切点。在@Transactional的实现中,同样如此,spring为我们定义了以 @Transactional 注解为植入点的切点,这样才能知道@Transactional注解标注的方法需要被代理。
2、有了切面定义之后,在spring的bean的初始化过程中,就需要对实例化的bean进行代理,并且生成代理对象。
3、生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional注解的切面逻辑类似于@Around,在spring中是实现一种类似代理逻辑。
@Transactional作用
根据上面的原理猜想,下面简单介绍每个步骤的源码以进行验证。
首先是@Transactional,作用是定义代理植入点。【aop实现原理分析】中,分析知道代理对象创建的通过BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInstantiation方法来实现个,如果需要进行代理,那么在这个方法就会返回一个代理对象给容器,同时判断植入点也是在这个方法中。
那么下面开始分析,在配置好注解驱动方式的事务管理之后,spring会在ioc容器创建一个BeanFactoryTransactionAttributeSourceAdvisor实例,这个实例可以看作是一个切点,
在判断一个bean在初始化过程中是否需要创建代理对象,都需要验证一次
BeanFactoryTransactionAttributeSourceAdvisor
是否是适用这个bean的切点。如果是,就需要创建代理对象,并且把
BeanFactoryTransactionAttributeSourceAdvisor
实例注入到代理对象中。
其中【aop实现原理分析】知道在AopUtils#findAdvisorsThatCanApply中判断切面是否适用当前bean,可以在这个地方断点分析调用堆栈,AopUtils#findAdvisorsThatCanApply一致调用,最终通过以下代码判断是否适用切点。

AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass) 这里可以根据参数打上条件断点进行调试分析调用栈,targetClass就是目标class
…一系列调用
最终SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)

@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
//这里就是分析Method是否被@Transactional注解标注,有的话,不用说BeanFactoryTransactionAttributeSourceAdvisor适配当前bean,进行代理,并且注入切点
//BeanFactoryTransactionAttributeSourceAdvisor
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
上面就是判断是否需要根据@Transactional进行代理对象创建的判断过程。@Transactional的作用一个就是标识方法需要被代理,一个就是携带事务管理需要的一些属性信息。
动态代理逻辑实现
【aop实现原理分析】中知道,aop最终的代理对象的代理方法是

DynamicAdvisedInterceptor#intercept

所以我们可以在这个方法断点分析代理逻辑。
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
//follow
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
通过分析 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)返回的是TransactionInterceptor,利用TransactionInterceptor是如何实现代理逻辑调用的?
跟踪new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
发现最终是调用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,从上面可以看到CglibMethodInvocation是包装了目标对象的方法调用的所有必须信息,因此,在TransactionInterceptor#invoke里面也是可以调用目标方法的,并且还可以实现类似@Around的逻辑,在目标方法调用前后继续注入一些其他逻辑,比如事务管理逻辑。
TransactionInterceptor–最终事务管理者
下面看代码。
TransactionInterceptor#invoke
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
继续跟踪invokeWithinTransaction,下面的代码中其实就可以看出一些逻辑端倪,就是我们猜想的实现方式,事务管理。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {

// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//开启事务
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
//方法调用
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}

else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});

// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}

总结

最终可以总结一下整个流程,跟开始的猜想对照。
分析源码后对照

作者:一撸向北

blog.csdn.net/qq_20597727/article/details/84868035

往期精选  点击标题可跳转

【013期】你还在 new 对象吗?Java8 通用 Builder 了解一下?

【014期】你真的了解 ConcurrentHashMap 数据结构吗?

【015期】MySQL 数据库与 Redis 缓存如何实现最终一致性的四种方案?!

【016期】面试官问:a==1 && a==2 && a==3 是 true 还是 false?

【017期】面试官问:Java 中 for、foreach、stream 哪个处理效率更高?

【018期】JDK1.8 中 HashMap 底层实现原理源码分析,你 get 到了吗?

【019期】告诉面试官,我能优化 Group By,而且知道得很深!

【020期】面试官问:Java 遍历 Map 集合有几种方式?效率如何?

【021期】面试官问:Java 中 new 一个对象的过程中发生了什么?这有些夸张了!

【022期】ArrayList 使用 forEach 遍历时删除元素会报错吗?

点个赞,就知道你“在看”!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存