Spring事务源码
2019-08-16 12:17:19来源:博客园 阅读 ()
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; // 方法返回
}
}
原文链接:https://www.cnblogs.com/bigshark/p/11324612.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Spring系列.ApplicationContext接口 2020-06-11
- springboot2配置JavaMelody与springMVC配置JavaMelody 2020-06-11
- 给你一份超详细 Spring Boot 知识清单 2020-06-11
- SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 2020-06-11
- 掌握SpringBoot-2.3的容器探针:实战篇 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash