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匹配的事务属性,或{@code null}如果没有找到
* @param method 目标方法。
*/
@Nullable
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}
通过注解,我们可以很明确的了解到,这个接口的工作就是:
- 获取事务的属性,通过类、方法进行扫描,提取属性。
那么,我们也可以猜到,TransactionInterceptor、TransactionAttributeSourceAdvisor
这两个类的作用就进行事务方法的拦截、以及事务属性的增强。
好了,解析的内容在上一步已经讲解了,接下来就讲解忽略的方法:AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
跳进源码:
public static void registerAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//如下registerAutoProxyCreatorIfNecessary是重点
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
既然我们在上一步中已经注册了三个bean,那么这个方法又是干嘛呢?再次深入栈:
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
可以看到,就是注册了一个InfrastructureAdvisorAutoProxyCreator.class
。我们进入这个类看看:
根据这个类的层次图,其中最重要的一点就是,简介实现了InstantiationAwareBeanPostProcessor
,而这个接口的最大特点就是在Spring中,所有的bean实例化时都会保证调用postProcessAfterInstantiation()
。
如下是在创建自动代理的抽象类中的这个方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
//构造key,由beanClassName_beanName组成。
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//判断是不是由于避免循环依赖创建的bean代理。
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
再跳进 wrapIfNecessary(bean, beanName, cacheKey);
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//已经处理过的直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//判断bean是否代表基础设施类,不应代理,或者配置了指定的bean,无需代理。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//这里就是重点了
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
以上的方法无非就是:
- 找出bean对应的增强器
- 根据找到的增强器创建代理
解析到这里,大家猜猜下一步是要干嘛?当然就是跟紧步伐,查找增强器的工作了:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
//解析上面的还需要如下两个:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//获取增强器
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//从增强器中找到匹配的、适应的增强器。
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
以上的工作就是查找匹配事务处理的增强器啦!
那么,接下来,将转入事务标签的提取工作:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 声明的方法域必须为public
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 首先尝试的是目标类中的方法。
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// 第二次尝试是目标类的事务属性。
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// 回退去看原始方法。其实这里就是接口中的方法
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// 回退去看接口的继承类的实现方法。
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
以上就是对事务标签的提取:
- 方法必须是public方法。
- 事务目标优先级的处理也是在这里定义:从方法到类,再到声明接口中的方法、再到接口的实现类。
好了,接下来进行解析事务注解:
//从findTransactionAttribute(method);继续挑,就能找到这个属性提取的方法
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
以上的注册解析工作一直jump、jump,终于解析到这里:
- 提取各种回滚设置
- 提取超时设置、传播行为、隔离级别等属性
?终于,事务的属性解析、提取工作告一段落了!
好了,接下来进行事务的增强工作的剖析,由``
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
//以下是声明式事务的处理
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//以下是编程式事务的处理
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
看了以上这个方法,我的感受是?,怎么这么长!那么总结一下:
- 分为声明式事务与编程式事务的处理
- 都是通过反射机制,执行对应的事务方法
- 都是在执行事务的过程中尝试捕捉异常,进行回滚并提交。
来到这里,好了,接下来就是进行事务的创建!:
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//重点在这里
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
以上进行的工作则是:
- 一些事务信息(一开始解析、提取的信息)的准备工作
- 调用
getTransaction(txAttr)
获取事务
那么接下来肯定就是去看看是怎么获取事务的,注意注释:
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
//對存在事務的處理
if (isExistingTransaction(transaction)) {
//判斷當前綫程是否存在事務。
//依據:當前綫程記錄的鏈接不爲空&&connectionHolder中的trans..Active屬性不爲空。
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//以下是對新事務的定義設置
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
//以下是不存在事務時候進行的判定,判定傳播行爲的要求。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
} else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//沒有可以挂起的事務,即挂空。
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
//添加了事務開啓時候的信息打印
System.err.println("開啓新事務,并進行處理:隔離級別為"+definition.getIsolationLevel()+
"傳播級別為:"+definition.getPropagationBehavior()+"***");
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
} catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
} else {
// 创建“空”事务:没有实际事务,但可能是同步。
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
事务的获取工作总结如下:
- 从当前线程绑定的资源查看当前线程是否存在事务,若不存在则创建新的事务并且设置事务的各种属性,并且挂空。
- 如果当前线程存在事务,调用
handleExistingTransaction(definition, transaction, debugEnabled)
进行处理。 - 如果事务的传播级别定义为不允许事务等,则会抛出异常,创建空事务。如果传播级别为要求新事务,则会挂断原有的事务,进行新事务的处理,之后再进行
resume()
重启原事务。
这个时候我们的剖析主线分为了两个:一个是如果已经存在事务,该怎么处理?一个是下一步操作,doBegin()
。
- 解析
handleExistingTransaction
/**
* 为现有事务创建TransactionStatus。
* 獲取事務時,處理現有的事務。
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
//如果是要求新事物,則挂斷原有事務。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
//挂起原有事務
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//dobegin裏面主要就是對連接的自動提交進行取消,設置事務狀態,同步狀態,超時設置等。
//以及綁定連接持有者到綫程上。
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
} catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
//如果是nested,則設置保存點。保存點的作用:一般外層的事務狀態不會影響内層,設置保存點,即外部也可以影響内部。
//這裏就是處理嵌入式事務。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
//如果是設置保存點,則沒有必要挂起原有事務。
//這就是爲什麽外層事務可以影響内層事務的提交的原因。
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
} else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
总结一下以上处理存在事务的工作:
- 根据传播行为,决定启用新事务或者嵌套事务或者不启用事务又或者共用当前事务。
- 如果是启用新事务,则会注入新事务的属性。
- 如果是JDBC3.0之后,那么嵌套事务才能使用,通过设置保存点的方式进行处理。
- 接下来就是
doBegin()
的工作了:
/**
* 此实现设置隔离级别但忽略超时。
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
//不存在連接,則新創建一個連接。
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//獲取連接并且設置同步狀態。
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
//設置隔離級別
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
//如有必要,切换到手动提交。这在某些JDBC驱动程序中非常昂贵,
//所以我们不想不必要地做这件事(例如,如果我们明确的话
//配置连接池以进行设置)。
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
//取消自動提交,方便接下來的提交。
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
//設置事務的活躍狀態。
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
//如何实现绑定线程:
//采用threadLocal定义一个map;
//添加资源进去,即每个线程都有独立的map,即该map资源与线程绑定。
//在这里:实际是将数据源~连接持有者存到线程的独立map。
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
以上注释其实已经足够清晰了,但是还是做一个总结:
- 如果存在连接则直接设置使用,不存在则创建连接
- 从Connection层次设置隔离级别
- 设置事务的状态
- 取消Connection的自动提交,由Spring管理事务的提交工作
- 将该事务的Connection持有者ConnectionHolder与当前线程进行
ThreadLocal
的资源绑定。(这里如果不懂线程资源绑定的,可以查看我的一篇关于ThreadLocal的博文)
通过以上的内容,事务架构我们已经了解得差不多了,就剩下回滚操作和Spring管理的提交工作
这是回滚操作,源码有点长,请跟着注释看:
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
//根据事务信息进行回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
//回滚的方法
public final void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}
//Spring的老套路,再跳下一层的实现
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
//这里就是对保存点的回滚工作的处理
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
//嵌套事务的回滚
status.rollbackToHeldSavepoint();
} else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
//普通事务的回滚
doRollback(status);
} else {
//以下是属于分布式事务的回滚,需要一个全局的回滚操作!
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
} else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
} else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
} catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
} finally {
cleanupAfterCompletion(status);
}
}
接下来就看看他们是怎么回滚的:
//直接调用JDBC底层connection的回滚。
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
} catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
以下是嵌套事务的回滚
public void rollbackToHeldSavepoint() throws TransactionException {
Object savepoint = getSavepoint();
if (savepoint == null) {
throw new TransactionUsageException(
"Cannot roll back to savepoint - no savepoint associated with current transaction");
}
getSavepointManager().rollbackToSavepoint(savepoint);
getSavepointManager().releaseSavepoint(savepoint);
setSavepoint(null);
}
//也是在JDBC底层的connection进行回滚。
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
conHolder.getConnection().rollback((Savepoint) savepoint);
conHolder.resetRollbackOnly();
}
catch (Throwable ex) {
throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
}
}
回滚的工作剖析完毕,那么就是提交工作了:
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
} catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
也是从JDBC层面进行提交。当然,在整个跳的过程,你会发现,有一些提交、回滚操作是属于jta的,jta是更为强大的分布式事务。在这里只讲解JDBC层面的提交工作。如果读者对JTA有兴趣,可以在TransactionImpl
了解。
好了,整个事务架构的源码剖析工作到此就完成了!过程就是:解析、提取、获取事务、事务处理。
四.嵌套事务的demo
这里只给出嵌套事务的几个例子,代码比较简单,只列出一个。
例1,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED,
1、如果ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,会共用同一个事务,如果出现异常,ServiceA.methodA和ServiceB.methodB作为一个整体都将一起回滚。
2、如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。ServiceA.methodA中是不受事务控制的。如果出现异常,ServiceB.methodB不会引起ServiceA.methodA的回滚。
例2,ServiceA.methodA的事务级别PROPAGATION_REQUIRED,ServiceB.methodB的事务级别PROPAGATION_REQUIRES_NEW,
调用ServiceB.methodB,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务。
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。
例3,ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_NESTED,
调用ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的子事务并设置savepoint
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB也将回滚。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。
以下是代码:
@Service
public class DataServiceImpl {
@Resource
private TestDao testDao;
@Autowired
private DataServiceImpl service;
/**
* 以下两个方法用于测试嵌入式事务。
*/
@Transactional(rollbackFor = {Exception.class, SQLException.class, RuntimeException.class}, propagation = Propagation.REQUIRED)
public void outterTransaction() {
testDao.add(1, "outter");
service.innerTransaction();
throw new RuntimeException("");
}
@Transactional(rollbackFor = {Exception.class, SQLException.class, RuntimeException.class}, propagation = Propagation.NESTED)
public void innerTransaction() {
testDao.add(2, "inner");
}
}
@Mapper
public interface TestDao {
@Insert("insert into t1 (id,time_come) values(#{id},#{t})")
void add(@Param("id")int id,@Param("t") String t);
}
//具体的表结构就是表名为t1 两列名为id,time_come。
五.总结
连续写了几个小时,简单总结一下:
- 事务的四大特性、五大隔离级别、七大传播行为
- 事务的源码剖析过程,可以发现这些隔离级别或者传播行为都体现在源码层面上
- 嵌套事务的概念以及demo
累了累了,休息休息。