1. 程式人生 > >Springboot原始碼分析之@Transactional

Springboot原始碼分析之@Transactional

摘要:

SpringBoot有多瞭解,其實就是看你對Spring Framework有多熟悉~ 比如SpringBoot大量的模組裝配的設計模式,其實它屬於Spring Framework提供的能力。SpringBoot大行其道的今天,基於XML配置的Spring Framework的使用方式註定已成為過去式。註解驅動應用,面向元資料程式設計已然成受到越來越多開發者的偏好了,畢竟它的便捷程度、優勢都是XML方式不可比擬的。

    @Configuration
    @ConditionalOnClass({PlatformTransactionManager.class})
    @AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
    @EnableConfigurationProperties({TransactionProperties.class})
    public class TransactionAutoConfiguration {
        public TransactionAutoConfiguration() {
        }
    
        @Bean
        @ConditionalOnMissingBean
        public TransactionManagerCustomizers platformTransactionManagerCustomizers(ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
            return new TransactionManagerCustomizers((Collection)customizers.orderedStream().collect(Collectors.toList()));
        }
    
        @Configuration
        @ConditionalOnBean({PlatformTransactionManager.class})
        @ConditionalOnMissingBean({AbstractTransactionManagementConfiguration.class})
        public static class EnableTransactionManagementConfiguration {
            public EnableTransactionManagementConfiguration() {
            }
    
            @Configuration
            @EnableTransactionManagement(
                proxyTargetClass = true
            )
            @ConditionalOnProperty(
                prefix = "spring.aop",
                name = {"proxy-target-class"},
                havingValue = "true",
                matchIfMissing = true
            )
          //預設採用cglib代理
            public static class CglibAutoProxyConfiguration {
                public CglibAutoProxyConfiguration() {
                }
            }
    
            @Configuration
            @EnableTransactionManagement(
                proxyTargetClass = false
            )
            @ConditionalOnProperty(
                prefix = "spring.aop",
                name = {"proxy-target-class"},
                havingValue = "false",
                matchIfMissing = false
            )
            public static class JdkDynamicAutoProxyConfiguration {
                public JdkDynamicAutoProxyConfiguration() {
                }
            }
        }
    
        @Configuration
      //當PlatformTransactionManager型別的bean存在並且當存在多個bean時指定為Primary的 PlatformTransactionManager存在時,該配置類才進行解析
        @ConditionalOnSingleCandidate(PlatformTransactionManager.class)
        public static class TransactionTemplateConfiguration {
            private final PlatformTransactionManager transactionManager;
    
            public TransactionTemplateConfiguration(PlatformTransactionManager transactionManager) {
                this.transactionManager = transactionManager;
            }
    
         // 由於TransactionAutoConfiguration是在DataSourceTransactionManagerAutoConfiguration之後才被解析處理的,而在DataSourceTransactionManagerAutoConfiguration中配置了transactionManager,因此, TransactionTemplateConfiguration 會被處理.
            @Bean
            @ConditionalOnMissingBean
            public TransactionTemplate transactionTemplate() {
                return new TransactionTemplate(this.transactionManager);
            }
        }
    }
PlatformTransactionManager後續章節會分析

提示:使用@EnableTransactionManagement註解前,請務必保證你已經配置了至少一個PlatformTransactionManager的Bean,否則會報錯。(當然你也可以實現TransactionManagementConfigurer來提供一個專屬的,只是我們一般都不這麼去做~~~)

開啟註解驅動

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({TransactionManagementConfigurationSelector.class})
    public @interface EnableTransactionManagement {
        boolean proxyTargetClass() default false;
        AdviceMode mode() default AdviceMode.PROXY;
        int order() default 2147483647;
    }

簡直和@EnableAsync註解的一模一樣。不同之處只在於@Import匯入器匯入的這個類.

對比一下

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AsyncConfigurationSelector.class)
    public @interface EnableAsync {
        // 支援自定義註解型別 去支援非同步~~~
        Class<? extends Annotation> annotation() default Annotation.class;
        boolean proxyTargetClass() default false;
        AdviceMode mode() default AdviceMode.PROXY;
        int order() default Ordered.LOWEST_PRECEDENCE;
    }

TransactionManagementConfigurationSelector

    public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
        public TransactionManagementConfigurationSelector() {
        }
    
        protected String[] selectImports(AdviceMode adviceMode) {
            switch(adviceMode) {
            case PROXY:
                return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[]{this.determineTransactionAspectClass()};
            default:
                return null;
            }
        }
    
        private String determineTransactionAspectClass() {
            return ClassUtils.isPresent("javax.transaction.Transactional", this.getClass().getClassLoader()) ? "org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration" : "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
        }
    }

依然可以看出和@EnableAsync匯入的AsyncConfigurationSelector如出一轍,都繼承自AdviceModeImportSelector,畢竟模式一樣,觸類旁通,一通百通~

AdviceModeImportSelector目前所知的三個子類是:AsyncConfigurationSelectorTransactionManagementConfigurationSelectorCachingConfigurationSelector。由此可見後面還會著重分析的Spring的快取體系@EnableCaching,模式也是和這個極其類似的~~~

AutoProxyRegistrar

它是個ImportBeanDefinitionRegistrar,可以實現自己向容器裡註冊Bean的定義資訊

    // @since 3.1
    public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
        private final Log logger = LogFactory.getLog(this.getClass());
    
        public AutoProxyRegistrar() {
        }
    
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            boolean candidateFound = false;
            // 這裡面需要特別注意的是:這裡是拿到所有的註解型別~~~而不是隻拿@EnableAspectJAutoProxy這個型別的
            // 原因:因為mode、proxyTargetClass等屬性會直接影響到代理得方式,而擁有這些屬性的註解至少有:
            // @EnableTransactionManagement、@EnableAsync、@EnableCaching等~~~~
            // 甚至還有啟用AOP的註解:@EnableAspectJAutoProxy它也能設定`proxyTargetClass`這個屬性的值,因此也會產生關聯影響~
            Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
            Iterator var5 = annTypes.iterator();
    
            while(var5.hasNext()) {
                String annType = (String)var5.next();
                AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
                if (candidate != null) {
                    Object mode = candidate.get("mode");
                    Object proxyTargetClass = candidate.get("proxyTargetClass");
                  // 如果存在mode且存在proxyTargetClass 屬性
                // 並且兩個屬性的class型別也是對的,才會進來此處(因此其餘註解相當於都擋外面了~)
                    if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {
                        candidateFound = true;
                        if (mode == AdviceMode.PROXY) {
                          
                        // 它主要是註冊了一個`internalAutoProxyCreator`,但是若出現多次的話,這裡不是覆蓋的形式,而是以優先順序的形式
                            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                          //看要不要強制使用CGLIB的方式(由此可以發現  這個屬性若出現多次,是會是覆蓋的形式)
                            if ((Boolean)proxyTargetClass) {
                                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                                return;
                            }
                        }
                    }
                }
            }
    
            if (!candidateFound && this.logger.isInfoEnabled()) {
                String name = this.getClass().getSimpleName();
                this.logger.info(String.format("%s was imported but no annotations were found having both 'mode' and 'proxyTargetClass' attributes of type AdviceMode and boolean respectively. This means that auto proxy creator registration and configuration may not have occurred as intended, and components may not be proxied as expected. Check to ensure that %s has been @Import'ed on the same class where these annotations are declared; otherwise remove the import of %s altogether.", name, name, name));
            }
        }
    }

跟蹤AopConfigUtils的原始碼你會發現,事務這塊向容器注入的是一個InfrastructureAdvisorAutoProxyCreator,並且看看是採用CGLIB還是JDK代理。它主要是讀取Advisor類,並對符合的bean進行二次代理。

ProxyTransactionManagementConfiguration

    @Configuration
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
        public ProxyTransactionManagementConfiguration() {
        }
    
        @Bean(
            name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
        )
        @Role(2)
        public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
            BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
            advisor.setTransactionAttributeSource(this.transactionAttributeSource());
            advisor.setAdvice(this.transactionInterceptor());
            if (this.enableTx != null) {
              // 順序由@EnableTransactionManagement註解的Order屬性來指定 預設值為:Ordered.LOWEST_PRECEDENCE
                advisor.setOrder((Integer)this.enableTx.getNumber("order"));
            }
    
            return advisor;
        }
    
        @Bean
        @Role(2)
        // TransactionAttributeSource 這種類特別像 `TargetSource`這種類的設計模式
        public TransactionAttributeSource transactionAttributeSource() {
            return new AnnotationTransactionAttributeSource();
        }
    
        @Bean
        @Role(2)
      // 事務攔截器,它是個`MethodInterceptor`,它也是Spring處理事務最為核心的部分
        // 請注意:你可以自己定義一個TransactionInterceptor(同名的),來覆蓋此Bean
        public TransactionInterceptor transactionInterceptor() {
            TransactionInterceptor interceptor = new TransactionInterceptor();
            interceptor.setTransactionAttributeSource(this.transactionAttributeSource());
            if (this.txManager != null) {
                interceptor.setTransactionManager(this.txManager);
            }
            return interceptor;
        }
    }
    @Configuration
    public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
        @Nullable
        protected AnnotationAttributes enableTx;
       
      // 此處:註解的預設的事務處理器(可議通過實現介面TransactionManagementConfigurer來自定義配置)
        // 因為事務管理器這個東西,一般來說全域性一個就行,但是Spring也提供了定製化的能力~~~
          @Nullable
        protected PlatformTransactionManager txManager;
    
        public AbstractTransactionManagementConfiguration() {
        }
    
        public void setImportMetadata(AnnotationMetadata importMetadata) {
            this.enableTx = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
          //這個註解@EnableTransactionManagement是必須的~~~~~~~~~~~~~~~~否則報錯了
            if (this.enableTx == null) {
                throw new IllegalArgumentException("@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
            }
        }
    
        @Autowired(
            required = false
        )
      // 這裡和@Async的處理一樣,配置檔案可以實現這個介面。然後給註解驅動的給一個預設的事務管理器~~~~
        void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
            if (!CollectionUtils.isEmpty(configurers)) {
              // 同樣的,最多也只允許你去配置一個~~~
                if (configurers.size() > 1) {
                    throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
                } else {
                    TransactionManagementConfigurer configurer = (TransactionManagementConfigurer)configurers.iterator().next();
                    this.txManager = configurer.annotationDrivenTransactionManager();
                }
            }
        }
    
        @Bean(
            name = {"org.springframework.transaction.config.internalTransactionalEventListenerFactory"}
        )
        @Role(2)
        public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
            return new TransactionalEventListenerFactory();
        }
    }

BeanFactoryTransactionAttributeSourceAdvisor

TransactionAttributeSourcePointcut

這個就是事務的匹配Pointcut切面,決定了哪些類需要生成代理物件從而應用事務。

    // 首先它的訪問許可權事default 顯示是給內部使用的
    // 首先它繼承自StaticMethodMatcherPointcut   所以`ClassFilter classFilter = ClassFilter.TRUE;` 匹配所有的類
    // 並且isRuntime=false  表示只需要對方法進行靜態匹配即可~~~~
    abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    
        // 方法的匹配  靜態匹配即可(因為事務無需要動態匹配這麼細粒度~~~)
        @Override
        public boolean matches(Method method, Class<?> targetClass) {
            // 實現瞭如下三個介面的子類,就不需要被代理了  直接放行
            // TransactionalProxy它是SpringProxy的子類。  如果是被TransactionProxyFactoryBean生產出來的Bean,就會自動實現此介面,那麼就不會被這裡再次代理了
            // PlatformTransactionManager:spring抽象的事務管理器~~~
            // PersistenceExceptionTranslator對RuntimeException轉換成DataAccessException的轉換介面
            if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
                    PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
                    PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
                return false;
            }
            
            // 重要:拿到事務屬性源~~~~~~
            // 如果tas == null表示沒有配置事務屬性源,那是全部匹配的  也就是說所有的方法都匹配~~~~(這個處理還是比較讓我詫異的~~~)
            // 或者 標註了@Transaction這樣的註解的方法才會給與匹配~~~
            TransactionAttributeSource tas = getTransactionAttributeSource();
            return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
        }   
        ...
        // 由子類提供給我,告訴事務屬性源~~~
        @Nullable
        protected abstract TransactionAttributeSource getTransactionAttributeSource();
    }

@Transactional

這個事務註解可以用在類上,也可以用在方法上。

  • 將事務註解標記到服務元件類級別,相當於為該服務元件的每個服務方法都應用了這個註解
  • 事務註解應用在方法級別,是更細粒度的一種事務註解方式

注意 : 如果某個方法和該方法所屬類上都有事務註解屬性,優先使用方法上的事務註解屬性。

另外,Spring 支援三個不同的事務註解 :

  • Spring 事務註解 org.springframework.transaction.annotation.Transactional
  • JTA事務註解 ·javax.transaction.Transactional·
  • EJB 3 事務註解 ·javax.ejb.TransactionAttribute·

自定義TransactionManagementConfigurer

    @Configuration
    @EnableTransactionManagement
    public class MyTransactionManagementConfigurer implements TransactionManagementConfigurer {
        @Bean
        public PlatformTransactionManager transactionManager(DataSource dataSource) {
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
            return dataSourceTransactionManager;
        }
    
        @Override
        public PlatformTransactionManager annotationDrivenTransactionManager() {
            return null;
        }
    }

@EnableAspectJAutoProxy和@EnableTransactionManagement優先順序?

  • @EnableAspectJAutoProxy會像容器注入AnnotationAwareAspectJAutoProxyCreator
  • @EnableTransactionManagement會像容器注入InfrastructureAdvisorAutoProxyCreator
    public abstract class AopConfigUtils {
        ...
        @Nullable
        private static BeanDefinition registerOrEscalateApcAsRequired(
                Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
            
            // 可以發現這裡有一個很巧妙的處理:會對自動代理建立器進行升級~~~~
            // 所以如果你第一次進來的是`InfrastructureAdvisorAutoProxyCreator`,第二次進來的是`AnnotationAwareAspectJAutoProxyCreator`,那就會取第二次進來的這個Class
            // 反之則不行。這裡面是維護的一個優先順序順序的,具體參看本類的static程式碼塊,就是順序  最後一個`AnnotationAwareAspectJAutoProxyCreator`才是最為強大的
            if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
                BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
                if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                    int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                    int requiredPriority = findPriorityForClass(cls);
                    if (currentPriority < requiredPriority) {
                        apcDefinition.setBeanClassName(cls.getName());
                    }
                }
                return null;
            }
    
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
            return beanDefinition;
        }
        ...
    }

從上面的分析可以知道:無論你的這些註解有多少個,無論他們的先後順序如何,它內部都有咯優先順序提升的機制來保證向下的覆蓋相容。因此一般情況下,我們使用的都是最高階的AnnotationAwareAspectJAutoProxyCreator這個自動代理建立器