一、前言 眾所周知,Python 不是一種執行效率較高的語言。此外在任何語言中,迴圈都是一種非常消耗時間的操作。假如任意一種簡單的單步操作耗費的時間為 1 個單位,將此操作重覆執行上萬次,最終耗費的時間也將增長上萬倍。 while 和 for 是 Python 中常用的兩種實現迴圈的關鍵字,它們的運 ...
本文內容
-
@Transactional事務使用
-
@EnableTransactionManagement 詳解
-
@Transactional事務屬性的解析
-
TransactionInterceptor 事務控制
聲明式事務使用和原理
聲明式的主要步驟
- 使用@EnableTransactionManagement啟用Spring 事務管理支持
- 使用@Transactional標識需要事務的方法會自動開啟事務
- 註入數據源和事務管理器
下麵通過案例演示一下上面的效果。
案例
-
使用@EnableTransactionManagement啟用Spring 事務管理支持,配置類上需要有@Configuration註解
@Configuration @ComponentScan @EnableTransactionManagement public class AppConfig {}
-
使用@Transactional標識需要事務的方法會自動開啟事務。
addUser
方法需要事務@Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; @Transactional public void addUser() { System.out.println("執行前記錄:" + jdbcTemplate.queryForList("SELECT * from t_user")); jdbcTemplate.update("insert into t_user (name) values (?)", "xx"); jdbcTemplate.update("insert into t_user (name) values (?)", "oo"); System.out.println("執行後記錄:" + jdbcTemplate.queryForList("SELECT * from t_user")); } }
-
註入數據源和事務管理器
@Configuration @ComponentScan @EnableTransactionManagement public class AppConfig { /** * 定義一個數據源 * @return */ @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(""); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://192.168.25.24:3306/xxx?characterEncoding=UTF-8"); dataSource.setUsername("root"); dataSource.setPassword("xxx"); return dataSource; } /** * 定義一個JdbcTemplate來執行sql * @param dataSource * @return */ @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } /** * 定義一個管理器 * @param dataSource * @return */ @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
-
測試程式
public class DeclarativeTest { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); userService.addUser(); context.close(); } }
輸出結果如下:
執行前記錄:[{id=1, name=test1-1}, {id=2, name=test1-2}, {id=3, name=xx}, {id=4, name=oo}, {id=5, name=xx}, {id=6, name=oo}] 執行後記錄:[{id=1, name=test1-1}, {id=2, name=test1-2}, {id=3, name=xx}, {id=4, name=oo}, {id=5, name=xx}, {id=6, name=oo}, {id=7, name=xx}, {id=8, name=oo}]
原理
@EnableTransactionManagement註解會開啟Spring自動管理事務的功能。開啟之後在Spring容器啟動的過程中,會攔截所有bean的創建過程,判斷bean 是否需要讓Spring來管理事務,如果需要那麼通過aop的方式創建代理對象。代理中會添加一個攔截器TransactionInterceptor
,攔截@Trasaction
標識方法的執行,在方法執行前後添加事務的功能。
下麵進行源碼分析,需要用到的前置只是是Spring Aop相關知識和編程式事務管理的知識,前面的文章有涉及,提前看一下。
@EnableTransactionManagement 詳解
@EnableTransactionManagement 會開啟Spring的事務管理功能,查看下源碼。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
/**
* 指示是否創建基於子類(CGLIB)的代理(true),而不是標準的基於Java介面的代理(false)。預設為false。
* 僅當mode()設置為AdviceMode.PROXY時適用。
*/
boolean proxyTargetClass() default false;
/**
* 指示應該如何應用事務通知。 預設值是AdviceMode.PROXY。
* 請註意,代理模式只允許通過代理攔截調用。同一類內的本地調用不會被攔截;
* 在本地調用中,對這種方法的Transactional註釋將被忽略,因為Spring的攔截器甚至不會在這樣的運行時場景中起作用。
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* 當在特定連接點上應用多個通知時,指示事務顧問程式的執行順序。 預設值是Ordered.LOWEST_PRECEDENCE
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
3個參數屬性值
- proxyTargetClass : 指示是否創建基於子類(CGLIB)的代理(true),而不是標準的基於Java介面的代理(false)
- mode:指示應該如何應用事務通知。 預設值是AdviceMode.PROXY。
- order: 當在特定連接點上應用多個通知時,指示事務顧問程式的執行順序。 預設值是Ordered.LOWEST_PRECEDENCE,最後處理事務攔截器。
TransactionManagementConfigurationSelector
重點是@Import(TransactionManagementConfigurationSelector.class)
,註入一些事務相關的bean到Spring容器中進行事務的管理控制。
根據EnableTransactionManagement的mode值選擇應該使用AbstractTransactionManagementConfiguration的哪個實現。
package org.springframework.transaction.annotation;
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* 此處是AdviceMode的作用,預設是用代理,另外一個是ASPECTJ
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// @1
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
我們的mode是 AdviceMode.PROXY ,所以走@1位置,註入AutoProxyRegistrar
和 ProxyTransactionManagementConfiguration
。
AutoProxyRegistrar註入 InfrastructureAdvisorAutoProxyCreator
AutoProxyRegistrar
的作用是註入一個InfrastructureAdvisorAutoProxyCreator
,用於攔截bean的創建過程,為需要的事務控制的bean 創建代理對象,這個類非常關鍵,後面詳細講。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
// 遍歷所有註解,找到有mode和proxyTargetClass的註解
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) {
// 註冊aop InfrastructureAdvisorAutoProxyCreator 不展開
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
// 強制設置proxyTargetClass=true後面使用cglib
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
}
ProxyTransactionManagementConfiguration
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
ProxyTransactionManagementConfiguration
代理事務配置,註冊事務需要用的一些類,而且Role=ROLE_INFRASTRUCTURE都是屬於內部級別的,如下:
BeanFactoryTransactionAttributeSourceAdvisor
事務屬性通知器,存放事務註解的方法相關的屬性TransactionAttributeSource
事務屬性源,就是事務註解的一些屬性,也用來解析事務註解屬性,實際是AnnotationTransactionAttributeSource
TransactionInterceptor
事務攔截器,該類包含與Spring底層事務API的集成。TransactionInterceptor
簡單地以正確的順序調用相關的超類方法,比如invokeWithinTransaction
。這個類非常關鍵,負責事務相關的AOP增強的。
小結
EnableTransactionManagement註解的作用主要註入了InfrastructureAdvisorAutoProxyCreator
負責攔截bean的創建過程為特定的bean創建代理對象,並通過TransactionInterceptor
事務攔截器來實現方法的事務控制。
@Transactional 詳解
該註解用於描述單個方法或類上的事務屬性。在類級別,該註釋作為預設值應用於聲明類及其子類的所有方法。註意,類級別它並不適用於類層次結構上的父類,也就是父類方法需要在本地重新聲明,以便參與子類級別的註釋。
註解的屬性的語義的具體信息,由 TransactionDefinition
和 TransactionAttribute
提供。
package org.springframework.transaction.annotation;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
/**
* 用來確定目標事務管理器bean
*/
@AliasFor("value")
String transactionManager() default "";
/**
* 事務傳播類型
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 事務隔離級別
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 事務超時
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 只讀事務
*/
boolean readOnly() default false;
/**
* 指定哪些異常類型必須導致事務回滾,指定Throwable的子類型;預設只回滾RuntimeException和Error
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 指示哪些異常類型必須導致事務回滾,這裡異常類名稱
*/
String[] rollbackForClassName() default {};
/**
* 指定哪些異常不進行回滾
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 指定哪些異常類型不進行回滾,異常類型名稱
*/
String[] noRollbackForClassName() default {};
}
@Transactional註解如何解析成事務屬性
AnnotationTransactionAttributeSource類
從類圖看我們關註AnnotationTransactionAttributeSource
通過SpringTransactionAnnotationParser
將@Transcation
轉成事務屬性供Spring事務處理使用。
- 如果
@Transcation
註解配置了屬性,轉換成RuleBasedTransactionAttribute
- 如果
@Transcation
註解沒有配置屬性,轉換成DefaultTransactionAttribute
,只有在拋出RuntimeException
和Error
時候才回滾
按照Spring源碼設計設計的一般套路我們看下右側的TransactionAttributeSource
介面和抽象類AbstractFallbackTransactionAttributeSource
TransactionAttributeSource 介面
public interface TransactionAttributeSource {
/**確定給定的類是否是TransactionAttributeSource元數據格式的事務屬性的候選類*/
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
/**解析給定方法的@Transaction事務屬性,如果方法是非事務性的,則返回null*/
@Nullable
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}
AbstractFallbackTransactionAttributeSource類
先看getTransactionAttribute()
方法
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 判斷method所在的class是不是Object類型
if (method.getDeclaringClass() == Object.class) {
return null;
}
// First, see if we have a cached value.
// 構建緩存key
Object cacheKey = getCacheKey(method, targetClass);
// 從緩存中獲取 @1
TransactionAttribute cached = this.attributeCache.get(cacheKey);
// 有緩存,不會每次computeTransactionAttribute
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
// 判斷緩存中的對象是不是空事務屬性的對象
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
// 存在就直接返回事務屬性
return cached;
}
}
else {
// We need to work it out.
// 查找我們的事務註解
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
// 若解析出來的事務註解屬性為空
if (txAttr == null) {
// 往緩存中存放空事務註解屬性
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
// 我們執行方法的描述符:包名+類名+方法名
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
// 把方法描述設置到事務屬性上去
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
}
if (logger.isTraceEnabled()) {
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
// 加入緩存
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
緩存中有類對應方法的事務屬性就直接返回,沒有就先解析@1再緩存起來。
computeTransactionAttribute
方法
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// 首先判斷方法是否是public,預設是支持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代表實現類中的方法
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
// 優先方法上解析的事務註解的屬性,會去找父類或者介面的方法
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
// 如果沒有,再嘗試聲明該方法的類上註解屬性,會去父類或者介面找
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
// 如果指定方法不等於方法
if (specificMethod != method) {
// Fallback is to look at the original method.
// 查找介面方法
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
// 到介面中的類中去尋找
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
查找事務屬性的查找順序如下:
- 特定的目標方法,會去找父類或者介面的方法
- 目標類,會去找父類或者介面
- 聲明的方法
- 聲明方法所在的類
AnnotationTransactionAttributeSource#determineTransactionAttribute()
方法委托給SpringTransactionAnnotationParser
解析給定類或是方法上的@Transactional
註解的事務屬性
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
// 獲取我們的註解解析器
for (TransactionAnnotationParser parser : this.annotationParsers) {
// 通過註解解析器去解析我們的元素(方法或者類)上的註解
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
接下來看下是如何解析和包裝的SpringTransactionAnnotationParser#parseTransactionAnnotation()
方法。
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
// 從element對象中獲取@Transactional註解,然後把註解屬性封裝到了AnnotationAttributes
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
// 解析出真正的事務屬性對象
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
分析2個點:
AnnotatedElementUtils.findMergedAnnotationAttributes()
負責解析目標類或目標方法上的@Transactional
,會向上找父類或是介面的parseTransactionAnnotation()
方法包裝成TransactionAttribute
看下是如何包裝轉換的。
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
// 創建一個基礎規則的事務屬性對象
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 解析@Transactionl上的傳播行為
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
// 解析@Transactionl上的隔離級別
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
// 解析@Transactionl上的事務超時事件
rbta.setTimeout(attributes.getNumber("timeout").intValue());
// 解析readOnly
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 解析@Transactionl上的事務管理器的名稱
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;
}
至此,@Transactional
是如何變成RuleBasedTransactionAttribute
已經很清晰了。
如何自動生成代理對象
這部分和之前的聲明式AOP的源碼分析是一樣的過程,通過類圖過一下。
-
誰負責創建代理?
InfrastructureAdvisorAutoProxyCreator
繼承我們熟悉的AbstractAdvisorAutoProxyCreator
類,是個BeanPostProcessor
,在Spring容器啟動的過程中,會攔截bean的創建過程,為需要事務支持的bean生成代理對象。 -
誰負責判斷bean是否需要代理?
BeanFactoryTransactionAttributeSourceAdvisor
是個Advisor,組合了切點和通知。哪些bean需要代理滿足增強由切點TransactionAttributeSourcePointcut
來通過TransactionAttributeSource
來判定bean的類或是方法上是否有@Transactional
註解。 -
誰負責實際的事務增強工作?
TransactionInterceptor 繼承
MethodInterceptor
是個攔截器,負責攔截代理對象目標方法,在前後增加事務控制的邏輯。這個類下麵進行詳細分析。
TransactionInterceptor 如何進行事務控制
TransactionInterceptor類
Spring中聲明式事務時通過AOP的方式實現的,事務方法的執行最終都會由TransactionInterceptor
的invoke()
攔截增強的。
package org.springframework.transaction.interceptor;
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
// 獲取我們的代理對象的class屬性
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
/**
* 以事務的方式調用目標方法
* 在這埋了一個鉤子函數 用來回調目標方法的
*/
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
事務的實現是委托給TransactionAspectSupport
父類實現的。
TransactionAspectSupport類
invokeWithinTransaction() 方法
基於環繞通知的實現事務控制,委托給該類上的其他幾個模板方法,其實裡面主要內容就是編程式的事務控制了。這是個模板方法,主要功能點如下:
- 如何獲取事務管理器對象
- 通過事務管理器開啟事務
- 執行目標方法
- 方法異常如何完成事務
- 正常返回如何完成事務提交
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 獲取我們的事務屬性源對象
TransactionAttributeSource tas = getTransactionAttributeSource();
// 通過事務屬性源對象獲取到當前方法的事務屬性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// @1獲取我們配置的事務管理器對象
final TransactionManager tm = determineTransactionManager(txAttr);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 獲取連接點的唯一標識 類名+方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 聲明式事務處理
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// @2創建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 執行被增強方法,調用具體的處理邏輯
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//@3 異常回滾 如何走?可能只需提交,也可能只需回滾,這個取決於事務的配置
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除事務信息,恢複線程私有的老的事務信息
cleanupTransactionInfo(txInfo);
}
//成功後提交,會進行資源儲量,連接釋放,恢復掛起事務等操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
determineTransactionManager() 事務管理器獲取
查找和獲取的順序是:
- 先看@Transactional中是否通過value或者transactionManager指定了事務管理器
- TransactionInterceptor.transactionManagerBeanName是否有值,如果有,將通過這個值查找事務管理器
- TransactionInterceptor.transactionManager是否有值,如果有則返回,這個是通過容器TransactionManagementConfigurer介面設置到TransactionInterceptor中的
- 如果上面3種都沒有,將從Spring容器中查找TransactionManager類型的作為預設事務管理器
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
// txAttr == null || this.beanFactory == null ,返回攔截器中配置的事務管理器
if (txAttr == null || this.beanFactory == null) {
return getTransactionManager();
}
//qualifier就是@Transactional註解中通過value或者transactionManager來指定事務管理器的bean名稱
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
//從spring容器中查找[beanName:qualifier,type:TransactionManager]的bean
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
//從spring容器中查找[beanName:this.transactionManagerBeanName,type:TransactionManager]的bean
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
//最後通過類型TransactionManager在spring容器中找事務管理器
TransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
createTransactionIfNecessary() 創建並開啟事務
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果沒有名稱指定則使用方法唯一標識,並使用DelegatingTransactionAttribute封裝txAttr
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) {
// @1獲取TransactionStatus事務狀態信息
status = tm.getTransaction(txAttr);
}
}
// @2根據指定的屬性與status準備一個TransactionInfo,
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
分析如下:
- @1獲取TransactionStatus事務狀態信息,也就是是編程式事務的創建和開啟。
- @2TransactionInfo生成
prepareTransactionInfo()
方法創建事務信息並綁定到當前線程
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
// 創建事務信息
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// The transaction manager will flag an error if an incompatible tx already exists.
// 設置新事務狀態
txInfo.newTransactionStatus(status);
}
// 事務信息綁定到當前線程
txInfo.bindToThread();
return txInfo;
}
事務信息是有哪些內容?簡單過一下內部類TransactionInfo
/**
* 用於保存事務信息的不透明對象。子類必須將其傳遞迴該類的方法,但不能看到其內部
*/
protected static final class TransactionInfo {
/** 事務管理器 */
@Nullable
private final PlatformTransactionManager transactionManager;
/** 事務屬性 */
@Nullable
private final TransactionAttribute transactionAttribute;
/** 切點標識名 */
private final String joinpointIdentification;
/** 事務狀態 */
@Nullable
private TransactionStatus transactionStatus;
/** 舊的事務信息 */
@Nullable
private TransactionInfo oldTransactionInfo;
/**
* 綁定新事務到當前線程,舊的會被保存
*/
private void bindToThread() {
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
/**
* 恢複線程中舊事務信息
*/
private void restoreThreadLocalStatus() {
transactionInfoHolder.set(this.oldTransactionInfo);
}
}
completeTransactionAfterThrowing() 異常後完成事務
/**
* 如果支持回滾的話就進行回滾,否則就處理提交,提交裡面如果TransactionStatus.isRollbackOnly()=true的話也會進行回滾處理
*/
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);
}
// @1判斷事務是否需要回滾
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 進行回滾
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
// @2通過事務管理器提交事務
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}
}
異常後如果匹配上我們@Transaction
指定的異常類型,在調用事務管理器進行事務回滾,否則通過事務管理器進行提交事務。
commitTransactionAfterReturning 正常完成事務
通過事務管理器進行事務提交。
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
小結
TransactionInterceptor 對事務控制包括開啟、提交、回滾等操作,其實都是通過事務管理器進行的,這和編程式事務管理是一樣的。
總結
本文進行了Spring中@Transactional聲明事務的源碼解析,結合了聲明式AOP的源碼分析和編程式事務管理的源碼分析。總結下過程是就是通過BeanPostProcessor攔截bean創建過程自動創建代理對象,通過TransactionInterceptor 環繞通知增強目標方法,在目標方法執行前後增加事務的控制邏輯。
知識分享,轉載請註明出處。學無先後,達者為先!