@Transactional
注解的逻辑是通过动态代理来实现的,而生成这个动态代理类分成了两步:
1、向spring容器注册事务相关的切面逻辑
2、根据切面逻辑生成动态代理
下面围绕这两点来看下Springboot里的实现原理
注册事务切面逻辑
切面逻辑里有三个概念:
Pointcut
:负责告诉spring容器哪个类需要增强
Advise
:具体的切面逻辑,这里就是根据异常进行commit或者回滚的相关逻辑
Advisor
:封装了Advise
和Pointcut
的类
事务相关的这三个对象是由Springboot自动装载的(自动装载组件原理可以看这里),流程如下:
1、扫描spring-boot-autoconfigure依赖包下的META-INF/spring.factories,加载文件里的TransactionAutoConfiguration
类
2、解析TransactionAutoConfiguration
类里的@EnableTransactionManagement
注解,根据该注解上的@Import
加载TransactionManagementConfigurationSelector
类
3、通过TransactionManagementConfigurationSelector
类的selectImports
方法加载ProxyTransactionManagementConfiguration
类
4、 ProxyTransactionManagementConfiguration
类通过@Bean
加载BeanFactoryTransactionAttributeSourceAdvisor
类,该类实现了Pointcut
,并且在该类里注入了实现了Advise
接口的TransactionInterceptor
类
BeanFactoryTransactionAttributeSourceAdvisor
类图如下:
Pointcut
的匹配逻辑就是看这个方法有没有被@Transactional
注解标注,最终会调用到SpringTransactionAnnotationParser
类的parseTransactionAnnotation
方法里,有兴趣的同学可以在这里打上断点看下调用链,debug图如下:
这里主要看下事务的核心逻辑,这个核心逻辑就在实现了Advise
接口的TransactionInterceptor
类的invoke
方法里,这里看下这里的源码:
1 public Object invoke(MethodInvocation invocation) throws Throwable { 2 Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null; 3 Method var10001 = invocation.getMethod(); 4 invocation.getClass(); 5 // 调用事务逻辑 6 return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed); 7 } 8 9 @Nullable 10 protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable { 11 TransactionAttributeSource tas = this.getTransactionAttributeSource(); 12 // 获取改方法上的事务配置,包括传播级别、异常信息等配置 13 TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null; 14 // 事务管理器,负责生成事务上下文信息,比如开启事务、获取数据库链接等逻辑 15 TransactionManager tm = this.determineTransactionManager(txAttr); 16 ... 17 PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm); 18 String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr); 19 // 根据传播级别配置,看是否需要新建事务 20 TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); 21 22 Object retVal; 23 // 通过try catch捕获异常来实现回滚逻辑 24 try { 25 // 调用真正的dao层逻辑 26 retVal = invocation.proceedWithInvocation(); 27 } catch (Throwable var18) { 28 // 根据@Transactional配置的异常来决定是否回滚 29 this.completeTransactionAfterThrowing(txInfo, var18); 30 throw var18; 31 } finally { 32 // 结束当前的事务,信息是保存在ThreadLocal里 33 this.cleanupTransactionInfo(txInfo); 34 } 35 36 if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) { 37 TransactionStatus status = txInfo.getTransactionStatus(); 38 if (status != null && txAttr != null) { 39 retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status); 40 } 41 } 42 // 没有异常时,执行commit操作 43 this.commitTransactionAfterReturning(txInfo); 44 return retVal; 45 ... 46 47 }
总结下核心步骤如下:
通过动态代理为标注了@Transactional
注解的方法增加切面逻辑,而事务的上下文包括数据库链接都是通过ThreadLocal
来传递,在这个切面逻辑里主要做这几个事情:
- 获取方法上标注的注解的元数据,包括传播级别、异常配置等信息
- 通过
ThreadLocal
获取事务上下文,检查是否已经激活事务 - 如果已经激活事务,则根据传播级别配置,看是否需要新建事务(如果新建事务,会生成一个新的事务上下文对象
TransactionInfo
,并将上一个事务上下文赋值到新上下文的oldTransactionInfo属性上)代码位置在TransactionAspectSupport
类的prepareTransactionInfo
方法里的bindToThread
方法里 - 开启事务,先通过数据库连接池获取链接,关闭链接的
autocommit
,然后在try catch
里反射执行真正的dao
操作,通过异常情况来决定是commit
还是rollback
下面看下Springboot
怎么根据这三个对象来生成代理类的。
根据切面逻辑生成代理类
核心逻辑是Springboot
会自动装载AopAutoConfiguration
类(自动装载组件原理可以看这里),该类会根据加载到Spring容器里的Advisor
对象来找到需要增强的类,并生成代理类,核心步骤如下:
1、加载AopAutoConfiguration
类,根据proxyTargetClass
配置来选择是使用jdk的proxy还是cglib来生成动态代理类,截图如下:
2、解析@EnableAspectJAutoProxy
注解,通过该注解上的@Import
加载AspectJAutoProxyRegistrar
类
3、AspectJAutoProxyRegistrar
类会向Spring容器注册AnnotationAwareAspectJAutoProxyCreator
类,该类实现了BeanPostProcessor
接口,所以这个aop逻辑是在Springbean生成过程中通过后置处理器逻辑来实现的。
AnnotationAwareAspectJAutoProxyCreator
类图如下:
AspectJAutoProxyRegistrar
类的父类AbstractAutoProxyCreator
类的postProcessAfterInitialization
方法里,该方法实现的是后置处理器BeanPostProcessor
类,用来生成动态代理类,这里看下具体逻辑:
1 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { 2 if (bean != null) { 3 Object cacheKey = this.getCacheKey(bean.getClass(), beanName); 4 if (this.earlyProxyReferences.remove(cacheKey) != bean) { 5 // 生成动态代理类 6 return this.wrapIfNecessary(bean, beanName, cacheKey); 7 } 8 } 9 10 return bean; 11 } 12 13 14 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { 15 ... 16 // 遍历注册到spring容器内的advisor,根据class信息找到匹配到的advisor 17 Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null); 18 if (specificInterceptors != DO_NOT_PROXY) { 19 this.advisedBeans.put(cacheKey, Boolean.TRUE); 20 // 生成动态代理类 21 Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); 22 this.proxyTypes.put(cacheKey, proxy.getClass()); 23 return proxy; 24 } else { 25 this.advisedBeans.put(cacheKey, Boolean.FALSE); 26 return bean; 27 } 28 ... 29 }
通过bean找到匹配的advisor的debug图如下,可以看到这里的BeanFactoryTransactionAttributeSourceAdvisor
就是上面注入的。
总结
可以看出Springboot里的@Transactional
之所以能起作用,是由两个自动装载类配合的,一个就是负责生成动态代理类的AopAutoConfiguration
类,一个就是TransactionAutoConfiguration
类,负责向Spring容器注册事务相关的Advisor
。这里也给我们提供了扩展,如果我们需要自定义自己的切面逻辑,只需要向spring容器注册自定义的Advisor
,定义好Pointcut
和Advise
就行了。
转自:https://juejin.cn/post/7003614270877335560
标签:事务,Springboot,Transactional,Object,逻辑,bean,详解,切面,null From: https://www.cnblogs.com/fnlingnzb-learner/p/16807712.html