1. 程式人生 > >spring事務詳解(二)原始碼詳解

spring事務詳解(二)原始碼詳解

系列目錄

spring事務詳解(三)使用樣例

spring事務詳解(四)測試驗證

spring事務詳解(五)總結提高

一、引子

在Spring中,事務有兩種實現方式:

  1. 程式設計式事務管理 程式設計式事務管理使用TransactionTemplate可實現更細粒度的事務控制。
  2. 申明式事務管理 基於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的commitrollbackOnException()異常回滾:呼叫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);