Spring的事务架构
其实作为一个作者,最大的难度就是怎么把一个复杂的东西简单化,这两天我也思考了很多,最后总结出大多数好的博文都是以一个总——分——总的结构,以及循序渐进的思想,进行一步步地讲解,接下来就将这种模式应用到这上面吧。
以下是今天的内容,分为五个部分:
- 事务的四大特性、五大隔离级别、七大传播行为
- 嵌套事务的概念
- 剖析事务架构源码
- 嵌套事务的demo
- 总结
一.事务的四大特性、五大隔离级别、七大传播行为
1.四大特性
- A:原子性,一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割。
- C:一致性,在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
- I:隔离性,数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- D:持久性,事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2.五大隔离级别
这里所说的五大隔离级别是针对在Spring的事务架构中,而在数据库中,只有后面四种级别。
为了接下来更好的讲解源码部分,先作一个铺垫,引入部分隔离级别的源码进行解读:
/**
* 使用基础数据存储的默认隔离级别。
* 所有其他级别对应于JDBC隔离级别。
*/
int ISOLATION_DEFAULT = -1;
/**
* 表示脏读,不可重复读和幻读
* 可以发生。
* <p>此级别允许一个事务更改的行被另一个事务读取
* 在该行中的任何更改已提交之前的事务(“脏读”)。
* 如果任何更改被回滚,则第二个事务将会检索到无效的行。
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* 表示防止脏读;不可重复的读取和
* 可以发生幻像读取。
* <p>此级别仅禁止事务读取行具有未提交的更改。
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 表示防止脏读和不可重复读;
* 可以发生幻像读取。
* <p>此级别禁止事务读取具有未提交更改的行
* 其中,它还禁止一个事务读取行的情况,
* 第二个事务改变行,第一个事务重新读取行,
* 第二次获得不同的值(“不可重复读取”)。
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 表示脏读,不可重复读和幻读被阻止。
* <p>此级别包含{@link #ISOLATION_REPEATABLE_READ}中的禁令
* 并进一步禁止一个事务读取满足{@code WHERE}条件所有行的情况,
* 第二个事务插入一行满足{@code WHERE}条件;且第一个事务
* 重新读取相同的条件,检索额外的“幻像”行在第二次阅读。
*
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
3.七大传播行为
这里也是列出源码进行解释:
//支持当前事务,如当前没有事务,则新建一个。
int PROPAGATION_REQUIRED = 0;
//支持当前事务,如当前没有事务,则非事务性执行
int PROPAGATION_SUPPORTS = 1;
//支持当前事务,如当前没有事务,则抛出异常(强制一定要在一个已经存在的事务中执行,业务方法不可独自发起自己的事务)
int PROPAGATION_MANDATORY = 2;
//始终新建一个事务,如当前原来有事务,则把原事务挂起。
int PROPAGATION_REQUIRES_NEW = 3;
//不支持当前事务,始终以非事务性方式执行,如当前事务存在,挂起该事务。
int PROPAGATION_NOT_SUPPORTED = 4;
//不支持当前事务;如果当前事务存在,则引发异常。
int PROPAGATION_NEVER = 5;
//如果当前事务存在,则在嵌套事务中执行,如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作(注意:当应用到JDBC时,只适用JDBC 3.0以上驱动)
int PROPAGATION_NESTED = 6;
传播行为是什么呢?可能有的人还不理解传播行为是什么,简化成一句话来说就是:这个事务的传播行为决定了它会重新创建一个事务去执行还是利用原有的事务,甚至不要事务。
可能有人对以上的PROPAGATION_NESTED
心存疑惑,什么是嵌套事务,接下来,将会讲解嵌套事务。
二.嵌套事务的概念
嵌套事务,切莫望文生义,认为就是事务中嵌套着另外一个事务。以下列出一个图来说明什么是嵌套事务:
如上,我们看到了两个事务,但是其实事务B也是属于事务A的一部分,这就叫嵌套事务,这里只有一条执行主线,那就是事务A,整个过程包括事务B的提交,都是由A代理的,这就是嵌套事务。
也许有人之前会认为,嵌套事务就是:
@Transactional
public void serviceA(){
//省略A的事务体
serviceB();
}
@Transactional
public void serviceB(){
//省略B的事务体
}
其实并不是这样的,事务B的传播行为必须为PROPAGATION_NESTED
才是嵌套事务。
三.剖析事务架构源码
讲了那么多特性和概念,接下来就要进入重点的源码剖析了!自上而下,进行一个源码分析,跟进步伐哦!
- 首先进行的是一个事务的解析工作,而解析的工作在Spring中都是由AnnotationDrivenBeanDefinitionParser类中的parse方法解析。
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
registerJtaTransactionAspect(element, parserContext);
}
}
else {
// mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
以上方法无非做了如下工作:
- 判断是AspectJ或者是代理模式(默认为代理),Spring的事务是以AOP为基础的。
- 根据对应模式,创建对应的事务配置。
?紧跟步伐,接下来,我们看看事务的配置是怎么创建的?
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
//下面这个方法是一个重点,但是却最容易被忽略!!!
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
// 创建TransactionAttributeSource定义。
RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// 创建TransactionInterceptor定义。
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// 创建TransactionAttributeSourceAdvisor定义。
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//这里就是将其中的两个bean注册到advisorDef上
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
}
?看到这一大堆代码,我头都晕了,但是理清思路,工作如下:
- 创建代理类
- 创建了三个bean,分别是:TransactionAttributeSource、TransactionInterceptor、TransactionAttributeSourceAdvisor。并且其中两个bean注册到Def的bean中。
简单介绍一下TransactionAttributeSource
,这是一个接口定义如下:
public interface TransactionAttributeSource {
/**
* 返回给定方法的事务属性,
* 或{@code null}如果方法是非事务性的。
* @param targetClass 目标类(可能是{@code null},在这种情况下,必须使用方法的声明类)
* @return TransactionAttribute匹配的事务属性,或{@