spring事務詳解(二)原始碼詳解
系列目錄
spring事務詳解(三)使用樣例
spring事務詳解(四)測試驗證
spring事務詳解(五)總結提高
一、引子
在Spring中,事務有兩種實現方式:
- 程式設計式事務管理: 程式設計式事務管理使用TransactionTemplate可實現更細粒度的事務控制。
- 申明式事務管理: 基於Spring AOP實現。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。
申明式事務管理不需要入侵程式碼,通過@Transactional就可以進行事務操作,更快捷而且簡單(尤其是配合spring boot自動配置,可以說是精簡至極!),且大部分業務都可以滿足,推薦使用。
其實不管是程式設計式事務還是申明式事務,最終呼叫的底層核心程式碼是一致的。本章分別從程式設計式、申明式入手,再進入核心原始碼貫穿式講解。
二、事務原始碼
2.1 程式設計式事務TransactionTemplate
全路徑名是:org.springframework.transaction.support.TransactionTemplate。看包名也知道了這是spring對事務的模板類。(spring動不動就是各種Template...),看下類圖先:
一看,喲西,實現了TransactionOperations、InitializingBean這2個介面(熟悉spring原始碼的知道這個InitializingBean又是老套路),我們來看下介面原始碼如下:
1 public interface TransactionOperations {
2
3 /**
4 * Execute the action specified by the given callback object within a transaction.
5 * <p>Allows for returning a result object created within the transaction, that is,
6 * a domain object or a collection of domain objects. A RuntimeException thrown
7 * by the callback is treated as a fatal exception that enforces a rollback.
8 * Such an exception gets propagated to the caller of the template.
9 * @param action the callback object that specifies the transactional action
10 * @return a result object returned by the callback, or {@code null} if none
11 * @throws TransactionException in case of initialization, rollback, or system errors
12 * @throws RuntimeException if thrown by the TransactionCallback
13 */
14 <T> T execute(TransactionCallback<T> action) throws TransactionException;
15
16 }
17
18 public interface InitializingBean {
19
20 /**
21 * Invoked by a BeanFactory after it has set all bean properties supplied
22 * (and satisfied BeanFactoryAware and ApplicationContextAware).
23 * <p>This method allows the bean instance to perform initialization only
24 * possible when all bean properties have been set and to throw an
25 * exception in the event of misconfiguration.
26 * @throws Exception in the event of misconfiguration (such
27 * as failure to set an essential property) or if initialization fails.
28 */
29 void afterPropertiesSet() throws Exception;
30
31 }
如上圖,TransactionOperations這個介面用來執行事務的回撥方法,InitializingBean這個是典型的spring bean初始化流程中(飛機票:Spring IOC(四)總結昇華篇)的預留介面,專用用來在bean屬性載入完畢時執行的方法。
回到正題,TransactionTemplate的2個介面的方法做了什麼?
1 @Override
2 public void afterPropertiesSet() {
3 if (this.transactionManager == null) {
4 throw new IllegalArgumentException("Property 'transactionManager' is required");
5 }
6 }
7
8
9 @Override
10 public <T> T execute(TransactionCallback<T> action) throws TransactionException { // 程式設計式事務
11 if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
12 return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
13 }// 宣告式事務
14 else {// 1.獲取事務狀態
15 TransactionStatus status = this.transactionManager.getTransaction(this);
16 T result;
17 try {// 2.執行業務邏輯
18 result = action.doInTransaction(status);
19 }
20 catch (RuntimeException ex) {
21 // 應用執行時異常 -> 回滾
22 rollbackOnException(status, ex);
23 throw ex;
24 }
25 catch (Error err) {
26 // Error異常 -> 回滾
27 rollbackOnException(status, err);
28 throw err;
29 }
30 catch (Throwable ex) {
31 // 未知異常 -> 回滾
32 rollbackOnException(status, ex);
33 throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
34 }// 3.事務提交
35 this.transactionManager.commit(status);
36 return result;
37 }
38 }
如上圖所示,實際上afterPropertiesSet只是校驗了事務管理器不為空,execute()才是核心方法,execute主要步驟:
1.getTransaction()獲取事務,原始碼見3.3.1
2.doInTransaction()執行業務邏輯,這裡就是使用者自定義的業務程式碼。如果是沒有返回值的,就是doInTransactionWithoutResult()。
3.commit()事務提交:呼叫AbstractPlatformTransactionManager的commit,rollbackOnException()異常回滾:呼叫AbstractPlatformTransactionManager的rollback(),事務提交回滾,原始碼見3.3.3
2.2 申明式事務@Transactional
1.AOP相關概念
申明式事務使用的是spring AOP,即面向切面程式設計。(什麼❓你不知道什麼是AOP...一句話概括就是:把業務程式碼中重複程式碼做成一個切面,提取出來,並定義哪些方法需要執行這個切面。其它的自行百度吧...)AOP核心概念如下:
- 通知(Advice):定義了切面(各處業務程式碼中都需要的邏輯提煉成的一個切面)做什麼what+when何時使用。例如:前置通知Before、後置通知After、返回通知After-returning、異常通知After-throwing、環繞通知Around.
- 連線點(Joint point):程式執行過程中能夠插入切面的點,一般有多個。比如呼叫方式時、丟擲異常時。
- 切點(Pointcut):切點定義了連線點,切點包含多個連線點,即where哪裡使用通知.通常指定類+方法 或者 正則表示式來匹配 類和方法名稱。
- 切面(Aspect):切面=通知+切點,即when+where+what何時何地做什麼。
- 引入(Introduction):允許我們向現有的類新增新方法或屬性。
- 織入(Weaving):織入是把切面應用到目標物件並建立新的代理物件的過程。
2.申明式事務
由於採用申明式@Transactional這種註解的方式,那麼我們從springboot 容器啟動時的自動配置載入(spring boot容器啟動詳解)開始看。在/META-INF/spring.factories中配置檔案中查詢,如下圖:
載入2個關於事務的自動配置類:
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,jta咱們就不看了,看一下TransactionAutoConfiguration這個自動配置類:
1 @Configuration 2 @ConditionalOnClass(PlatformTransactionManager.class) 3 @AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, 4 DataSourceTransactionManagerAutoConfiguration.class, 5 Neo4jDataAutoConfiguration.class }) 6 @EnableConfigurationProperties(TransactionProperties.class) 7 public class TransactionAutoConfiguration { 8 9 @Bean 10 @ConditionalOnMissingBean 11 public TransactionManagerCustomizers platformTransactionManagerCustomizers( 12 ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) { 13 return new TransactionManagerCustomizers(customizers.getIfAvailable()); 14 } 15 16 @Configuration 17 @ConditionalOnSingleCandidate(PlatformTransactionManager.class) 18 public static class TransactionTemplateConfiguration { 19 20 private final PlatformTransactionManager transactionManager; 21 22 public TransactionTemplateConfiguration( 23 PlatformTransactionManager transactionManager) { 24 this.transactionManager = transactionManager; 25 } 26 27 @Bean 28 @ConditionalOnMissingBean 29 public TransactionTemplate transactionTemplate() { 30 return new TransactionTemplate(this.transactionManager); 31 } 32 } 33 34 @Configuration 35 @ConditionalOnBean(PlatformTransactionManager.class) 36 @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) 37 public static class EnableTransactionManagementConfiguration { 38 39 @Configuration 40 @EnableTransactionManagement(proxyTargetClass = false) 41 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) 42 public static class JdkDynamicAutoProxyConfiguration { 43 44 } 45 46 @Configuration 47 @EnableTransactionManagement(proxyTargetClass = true) 48 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) 49 public static class CglibAutoProxyConfiguration { 50 51 } 52 53 } 54 55 }
TransactionAutoConfiguration這個類主要看:
1.2個類註解
@ConditionalOnClass(PlatformTransactionManager.class)即類路徑下包含PlatformTransactionManager這個類時這個自動配置生效,這個類是spring事務的核心包,肯定引入了。
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class }),這個配置在括號中的4個配置類後才生效。
2. 2個內部類
TransactionTemplateConfiguration事務模板配置類:
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)當能夠唯一確定一個PlatformTransactionManager bean時才生效。
@ConditionalOnMissingBean如果沒有定義TransactionTemplate bean生成一個。
EnableTransactionManagementConfiguration開啟事務管理器配置類:
@ConditionalOnBean(PlatformTransactionManager.class)當存在PlatformTransactionManager bean時生效。
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)當沒有自定義抽象事務管理器配置類時才生效。(即使用者自定義抽象事務管理器配置類會優先,如果沒有,就用這個預設事務管理器配置類)
EnableTransactionManagementConfiguration支援2種代理方式:
- 1.JdkDynamicAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = false),即proxyTargetClass = false表示是JDK動態代理支援的是:面向介面代理。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false),即spring.aop.proxy-target-class=false時生效,且沒有這個配置不生效。
- 2.CglibAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = true),即proxyTargetClass = true標識Cglib代理支援的是子類繼承代理。@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true),即spring.aop.proxy-target-class=true時生效,且沒有這個配置預設生效。
注意了,預設沒有配置,走的Cglib代理。說明@Transactional註解支援直接加在類上。
好吧,看了這麼多配置類,終於到了@EnableTransactionManagement這個註解了。
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(TransactionManagementConfigurationSelector.class) 5 public @interface EnableTransactionManagement { 6 7 //proxyTargetClass = false表示是JDK動態代理支援介面代理。true表示是Cglib代理支援子類繼承代理。 8 boolean proxyTargetClass() default false; 9 10 //事務通知模式(切面織入方式),預設代理模式(同一個類中方法互相呼叫攔截器不會生效),可以選擇增強型AspectJ 11 AdviceMode mode() default AdviceMode.PROXY; 12 13 //連線點上有多個通知時,排序,預設最低。值越大優先順序越低。 14 int order() default Ordered.LOWEST_PRECEDENCE; 15 16 }
重點看類註解@Import(TransactionManagementConfigurationSelector.class)
TransactionManagementConfigurationSelector類圖如下:
如上圖所示,TransactionManagementConfigurationSelector繼承自AdviceModeImportSelector實現了ImportSelector介面。
1 public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { 2 3 /** 4 * {@inheritDoc} 5 * @return {@link ProxyTransactionManagementConfiguration} or 6 * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and 7 * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively 8 */ 9 @Override 10 protected String[] selectImports(AdviceMode adviceMode) { 11 switch (adviceMode) { 12 case PROXY: 13 return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; 14 case ASPECTJ: 15 return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; 16 default: 17 return null; 18 } 19 } 20 21 }
如上圖,最終會執行selectImports方法匯入需要載入的類,我們只看proxy模式下,載入了AutoProxyRegistrar、ProxyTransactionManagementConfiguration2個類。
- AutoProxyRegistrar:
給容器中註冊一個 InfrastructureAdvisorAutoProxyCreator 元件;利用後置處理器機制在物件建立以後,包裝物件,返回一個代理物件(增強器),代理物件執行方法利用攔截器鏈進行呼叫;
- ProxyTransactionManagementConfiguration:就是一個配置類,定義了事務增強器。
AutoProxyRegistrar
先看AutoProxyRegistrar實現了ImportBeanDefinitionRegistrar介面,複寫registerBeanDefinitions方法,原始碼如下:
1 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 2 boolean candidateFound = false; 3 Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); 4 for (String annoType : annoTypes) { 5 AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); 6 if (candidate == null) { 7 continue; 8 } 9 Object mode = candidate.get("mode"); 10 Object proxyTargetClass = candidate.get("proxyTargetClass"); 11 if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && 12 Boolean.class == proxyTargetClass.getClass()) { 13 candidateFound = true; 14 if (mode == AdviceMode.PROXY) {//代理模式 15 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); 16 if ((Boolean) proxyTargetClass) {//如果是CGLOB子類代理模式 17 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 18 return; 19 } 20 } 21 } 22 } 23 if (!candidateFound) { 24 String name = getClass().getSimpleName(); 25 logger.warn(String.format("%s was imported but no annotations were found " + 26 "having both 'mode' and 'proxyTargetClass' attributes of type " + 27 "AdviceMode and boolean respectively. This means that auto proxy " + 28 "creator registration and configuration may not have occurred as " + 29 "intended, and components may not be proxied as expected. Check to " + 30 "ensure that %s has been @Import'ed on the same class where these " + 31 "annotations are declared; otherwise remove the import of %s " + 32 "altogether.", name, name, name)); 33 } 34 }
代理模式:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
最終呼叫的是:registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);基礎構建增強自動代理構造器
1 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { 2 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); //如果當前註冊器包含internalAutoProxyCreator 3 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {//org.springframework.aop.config.internalAutoProxyCreator內部自動代理構造器 4 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); 5 if (!cls.getName().equals(apcDefinition.getBeanClassName())) {//如果當前類不是internalAutoProxyCreator 6 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); 7 int requiredPriority = findPriorityForClass(cls); 8 if (currentPriority < requiredPriority) {//如果下標大於已存在的內部自動代理構造器,index越小,優先順序越高,InfrastructureAdvisorAutoProxyCreator index=0,requiredPriority最小,不進入 9 apcDefinition.setBeanClassName(cls.getName()); 10 } 11 } 12 return null;//直接返回 13 }//如果當前註冊器不包含internalAutoProxyCreator,則把當前類作為根定義 14 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); 15 beanDefinition.setSource(source); 16 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);//優先順序最高 17 beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 18 registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); 19 return beanDefinition; 20 }
如上圖,APC_PRIORITY_LIST列表如下圖:
1 /** 2 * Stores the auto proxy creator classes in escalation order. 3 */ 4 private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>(); 5 6 /** 7 * 優先順序上升list 8 */ 9 static { 10 APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); 11 APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); 12 APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);