Spring事务定义
我正在参与掘金新人创作活动,一起开启写作之路。
来自官方的定义:事务是一系列的数据库操作的集合,这些操作应该要不全部成功,要不全部失效,事务管理是关系型数据库确保数据的完整性和一致性的方法,其核心思想可以概况为ACID
- Atomicity(原子性)一个事务应该被视为一个单一的操作单元,这意味着整个操作序 列要么是成功的,要么是不成功的。
- Consistency(一致性)这表示数据库的引用完整性、表中唯一的主键等的一致性
- Isolation(隔离性)可能会使用相同的数据集同时进行许多事务处理。每个事务都应该与其他事务隔离,以防止数据损坏
- Durability(持久性)一旦事务完成,此事务的结果必须成为永久的,由于系统故障无法从数据库中删除
Spring事务原理
当我们了解完Spring事务定义后,我们可以猜想一下究竟Spring是怎么帮我们完成事务的,众所周知,我们要执行事务,只需要开启事务(注解开启/配置文件开启),然后添加@Transactional注解在类或者方法上面,就可以执行事务了,那么Spring究竟是如何完成这个过程的呢?带着疑问,我先来给大家说一下大概原理,如下图
如图所示,从宏观来说,Spring做的事情无非就是利用事务管理器创建了数据库连接,然后关闭自动提交,再执行我们的业务逻辑,最后提交或者回滚,讲到这里,有经验的童鞋想必已经明白是怎么肥事了,是的没错,它是利用了Spring的AOP,那么具体是怎么做的,下面我就来讲解一下
我们先从Spring事务配置开始说起,下面主要说的是通过注解的方式
- @EnableTransactionManagement
- @Transaction
@EnableTransactionManagement工作原理
SpringBoot项目上通过此注解开启事务AOP
@SpringBootApplication
@MapperScan("com.test.transaction.mapper")
@EnableTransactionManagement
public class TransactionApplication {
public static void main(String[] args) {
SpringApplication.run(TransactionApplication.class, args);
}
}
点击进入EnableTransactionManagement
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
上图可以看到此注解里面有三个属性,proxyTargerClass 实际上是决定它使用什么代理方式,false是使用jdk动态代理,true则使用CGLIB代理,但这个不是本章节主要说的内容,所以不深入探究,第二个是mode()默认为proxy,稍后会用的上,第三个order顾名思义就是一个执行顺序,除此之外,我们可以看到上面有个Import注解,这个注解主要是用于将类注册到Spring IOC容器中,那么我们看看它将什么类往Spring容器中进行了注册
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
public TransactionManagementConfigurationSelector() {
}
protected String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
case PROXY:
return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[]{this.determineTransactionAspectClass()};
default:
return null;
}
}
}
上面可以看到,对adviceMode进行了判断,而事务的默认是proxy,所以事务往Spring容器中注册了AutoProxyRegistrar和ProxyTransactionManagementConfiguration两个类,那么这两个类分别有什么作用呢?用下图作了一个概括
简单来说,AutoProxyRegistrar顾名思义就是开启了AOP代理 而ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:
- BeanFactoryTransactionAttributeSourceAdvisor:一个Advisor就是一个切面
- AnnotationTransactionAttributeSource:相当于Pointcut
- TransactionInterceptor:相当于中的 Advice,事务的拦截方法
AnnotationTransactionAttributeSource就是用来判断某个类上是否存在@Transactional注解, 或者判断某个方法上是否存在@Transactional注解的。
TransactionInterceptor就是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个 代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的 invoke()方法。
一句话总结:EnableTransactionManagement注解是用来开启事务aop的,@Transactional就是切点,有这个注解的类都会被代理
Spring事务执行原理
通过上面的分析我们可以对Spring事务有一个基本的了解,那么接下来,我们再通过源码来看看Spring事务执行的时候究竟做了什么事情(因为源码太多,本文仅挑选部分源码来进行解析,感兴趣的童鞋可以私底下继续研究源码)
首先我们来理一下Spring事务创建的整体流程:启动阶段:
- Spring创建完bean后会调用applyBeanPostProcessorsAfterInitialization
- getAdvicesAndXXForBean 判断这个bean是否需要AOP
- 寻找方法或类上面的@Transcational然后生成代理对象\
- computeTransactionAttribute会解析注解包装成TransactionAttrbute对象
方法执行阶段:
1.代理对象CglibAopProxy 执行事务拦截类 TransactionInterceptor的invoke方法
2.获取此类的注解属性然后调用determineTransactionManager看是否需要创建事务管理器,如果没有指定则使用默认的事务管理器
TransactionAttributeSource tas =this.getTransactionAttributeSource();
TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
TransactionManager tm = this.determineTransactionManager(txAttr);
3.调用createTransactionIfNecessary创建事务
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
4.使用事务管理器tm去创建一个事务连接
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction((TransactionDefinition)txAttr);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
}
}
5.通过 doGetConnection去创建事务,通过 getResource返回一个ConnectionHolder对象,
这个对象很关键,是通过线程的ThreadLocalMap来获取,也就是说我们的事务连接实际上是存在线程变量里面的,如果没有则创建并放入ThreadLocalMap,那么这里其实就可以和我们的传播机制结合起来,如是同一个事务的话则会用这里的事务
protected Object doGetTransaction() {
DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
6.事务创建完成后返回一个TransactionStatu对象,再通过prepareTransactionInfo将事务绑定到线程上,至此,事务已经创建完成
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {
TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.newTransactionStatus(status);
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No need to create transaction for [" + joinpointIdentification + "]: This method is not transactional.");
}
txInfo.bindToThread();
return txInfo;
}
7.后续则是获取此事务链接进行相应的数据库增删查改操作.
本章小结:本章主要是将Spring事务大概原理以及创建过程介绍了一遍,意犹未尽的童鞋可以去继续研究源码,而下个章节我会主要说一下Spring的传播机制,以及相关案例分析,相信会对各位小伙伴的工作学习都会带来一些帮助,也可以关注我的个人公众号,里面会有最新的技术分享内容,我是老道,专注框架原理,中间件,并发编程,只做最肝的干货分享,如果那么本章的分享到此结束,欢迎留言探讨