Spring事务源码
Posted bigshark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务源码相关的知识,希望对你有一定的参考价值。
启动事务
@EnableTransactionManagement 注解来启用事务能力。
参数解释
proxyTargetClass:默认为false,表示使用 JDK 的代理模式,true表示用 CGLib 的代理模式,仅在 mode 是 PROXY 时才有效。
mode:默认为PROXY,表示使用 AOP 代理来实现事务,ASPECTJ表示用 ASPECTJ 来实现事务,ASPECTJ 相比 PROXY 减少了一些使用限制,比如支持在同一个类内部方法调用。
order:事务通知执行的顺序,默认优先级最低
@Transactional 可以标注在类、接口、方法上,表示应用事务机制,
参数解释
value/transactionManager:如果有多个事务管理器时,指定一个事务管理器
propagation:事务传播类型,默认 REQUIRED。还有 SUPPORTS/MANDATORY/REQUIRES_NEW/NOT_SUPPORTED/NEVER/NESTED
isolation:事务隔离级别,默认为数据库的隔离级别,包括 READ_UNCOMMITTED/READ_COMMITTED/REPEATABLE_READ/SERIALIZABLE
timeout:事务超时时间
readOnly:是否只读,默认false
rollbackFor:指定哪种类型的异常需要回滚
rollbackForClassName:类似rollbackFor
noRollbackFor:指定哪种类型的异常不需要回滚
noRollbackForClassName:类似noRollbackFor
事务传播类型说明
枚举 | 描述 |
---|---|
REQUIRED | 如果当前存在事务,方法会在该事务中运行,否则创建一个新事务。 |
SUPPORTS | 方法可以不需要事务,但是如果存在当前事务,则会在该事务中运行。 |
MANDATORY | 方法必须运行在事务中,如果当前事务不存在,则抛出异常。 |
REQUIRES_NEW | 方法必须运行在它自己的事务中,如果存在当前事务,该事务将被挂起。 |
NOT_SUPPORTED | 方法不应该运行在事务中,如果存在当前事务,该事务将被挂起。 |
NEVER | 方法不应该运行在事务中,如果存在当前事务,则抛出异常。 |
NESTED | 如果当前已经存在一个事物,方法会在嵌套事务中运行;如果不存在事务,其行为和 PROPAGATION_REQUIRED 一致。 |
@EnableTransactionManagement 使用 @Import 导入 TransactionManagementConfigurationSelector,这个类继承了 AdviceModeImportSelector,也就是实现了 ImportSelector,所以配置类解析器会执行它的 selectImports() 方法,如下:
protected String[] selectImports(AdviceMode adviceMode)
switch (adviceMode)
case PROXY:
return new String[] AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName();
case ASPECTJ:
return new String[] determineTransactionAspectClass();
default:
return null;
这个方法根据通知模式 PROXY/ASPECTJ,选择导入不同的事务管理器。
以默认方式 PROXY 为例,导入 ProxyTransactionManagementConfiguration 和 AutoProxyRegistrar。
ProxyTransactionManagementConfiguration
从它的继承关系可以看出,它是 @Configuration 配置类,并且实现了 ImportAware 接口,所以它会被注入 Spring 容器,此外,它还会通过 @Bean 注入下面这些组件:
- AnnotationTransactionAttributeSource 解析 @Transactional 事务注解属性信息
- TransactionInterceptor 保存事务注解的属性信息和事务管理器信息,它是实现 MethodInterceptor 的事务拦截器,所以会在目标方法执行时执行拦截。
AutoProxyRegistrar
AutoProxyRegistrar 是 ImportBeanDefinitionRegistrar 的实现类,所以 ConfigurationClassBeanDefinitionReader 会执行它的方法 registerBeanDefinitions(),并且在执行后置处理器时,会回调 ImportAware.setImportMetadata(),将 @EnableTransactionManagement 配置属性缓存至 AnnotationAttributes。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes)
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null)
continue;
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass())
candidateFound = true;
if (mode == AdviceMode.PROXY)
// 注册 InfrastructureAdvisorAutoProxyCreator
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass)
// CGLib
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
- 如果是 PROXY 类型的通知模式,注册 InfrastructureAdvisorAutoProxyCreator 到 BeanFactory;
- 如果是 CGlib 代理,还要注册 AnnotationAwareAspectJAutoProxyCreator 到 BeanFactory;
查看 InfrastructureAdvisorAutoProxyCreator 的继承关系,实际上和 AnnotationAwareAspectJAutoProxyCreator 是类似的,都继承 AbstractAdvisorAutoProxyCreator,可以回看 Spring AOP源码。也就是说,事务实际上就是利用 AOP 原理来实现的:通过 JDK/CGLib 动态代理技术,给需要事务增强的类创建增强后的代理对象,待代理方法执行时,利用代理对象来实现事务机制。
执行代理对象
和 Spring AOP源码 类似,只不过拦截器链中是 TransactionInterceptor,所以会执行其 invoke() 方法:
@Override
public Object invoke(MethodInvocation invocation) throws Throwable
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable
// 获取 @Transactional 注解事务属性
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 如果没有指定,则按照类型获取事务管理器 PlatformTransactionManager
// 比如事务管理器 DataSourceTransactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 获取需要执行的目标方法
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager))
// 将事务信息绑定到线程局部变量
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try
// 类似方法的环绕通知,会执行目标方法
retVal = invocation.proceedWithInvocation();
catch (Throwable ex)
// 异常,使用事务管理器进行回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
finally
// 重置线程局部变量的事务信息
cleanupTransactionInfo(txInfo);
// 正常,使用事务管理器提交事务
commitTransactionAfterReturning(txInfo);
return retVal; // 方法返回
以上是关于Spring事务源码的主要内容,如果未能解决你的问题,请参考以下文章