本文內容
-
@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事務屬性源,就是事務注解的一些屬性,也用來決議事務注解屬性,實際是AnnotationTransactionAttributeSourceTransactionInterceptor事務攔截器,該類包含與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 環繞通知增強目標方法,在目標方法執行前后增加事務的控制邏輯,
知識分享,轉載請注明出處,學無先后,達者為先!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/453753.html
標籤:Java
上一篇:【SpringBoot實戰】開發入門--快速創建springboot程式
下一篇:Linux 搭建Apollo
