這一次搞懂Spring事務註解的解析方式
前言
事務我們都知道是什麼,而Spring事務就是在資料庫之上利用AOP提供宣告式事務和程式設計式事務幫助我們簡化開發,解耦業務邏輯和系統邏輯。但是Spring事務原理是怎樣?事務在方法間是如何傳播的?為什麼有時候事務會失效?接下來幾篇文章將重點分析Spring事務原始碼,讓我們徹底搞懂Spring事務的原理。
正文
XML標籤的解析
<tx:annotation-driven transaction-manager="transactionManager"/>
配置過事務的應該都不陌生,上面這個配置就是Spring開啟事務註解(@Transactional)支援的配置,而看過我之前文章的應該知道,這個帶字首的標籤叫自定義標籤,我在之前的文章也分析過自定義標籤的解析過程,所以這裡我直接找到對應的handler:
public class TxNamespaceHandler extends NamespaceHandlerSupport { static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager"; static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; static String getTransactionManagerName(Element element) { return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ? element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME); } @Override public void init() { registerBeanDefinitionParser("advice",new TxAdviceBeanDefinitionParser()); registerBeanDefinitionParser("annotation-driven",new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("jta-transaction-manager",new JtaTransactionManagerBeanDefinitionParser()); } }
可以看到對應的註解解析器就是AnnotationDrivenBeanDefinitionParser類,在該類中一定會有一個parse方法:
public BeanDefinition parse(Element element,ParserContext parserContext) { registerTransactionalEventListenerFactory(parserContext); String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // mode="aspectj" registerTransactionAspect(element,parserContext); if (ClassUtils.isPresent("javax.transaction.Transactional",getClass().getClassLoader())) { registerJtaTransactionAspect(element,parserContext); } } else { // mode="proxy" AopAutoProxyConfigurer.configureAutoProxyCreator(element,parserContext); } return null; }
首先拿到mode屬性的值判斷是使用AspectJ生成代理還是JDK生成代理,這裡我們主要看proxy模式,進入configureAutoProxyCreator方法:
public static void configureAutoProxyCreator(Element element,ParserContext parserContext) { // 註冊AOP的入口類 AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext,element); String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource = parserContext.extractSource(element); // Create the TransactionAttributeSource definition. // @Transactional註解的屬性封裝 RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. // AOP執行鏈 RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 拿到transaction-manager屬性的值 registerTransactionManager(element,interceptorDef); interceptorDef.getPropertyValues().add("transactionAttributeSource",new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); // Create the TransactionAttributeSourceAdvisor definition. RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); advisorDef.getPropertyValues().add("transactionAttributeSource",new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName",interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order",element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName,advisorDef); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef,sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef,interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef,txAdvisorBeanName)); parserContext.registerComponent(compositeDef); } }
這裡的流程比較長,但邏輯很簡單。首先來看註冊事務AOP入口類是哪個:
public static void registerAutoProxyCreatorIfNecessary( ParserContext parserContext,Element sourceElement) { // 將優先順序更高的AOP入口類放入到IOC容器中 BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary( parserContext.getRegistry(),parserContext.extractSource(sourceElement)); // 設定代理生成的方式以及是否快取代理類到當前執行緒 useClassProxyingIfNecessary(parserContext.getRegistry(),sourceElement); registerComponentIfNecessary(beanDefinition,parserContext); }
主要看registerAutoProxyCreatorIfNecessary方法:
public static BeanDefinition registerAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry,@Nullable Object source) { return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class,registry,source); } private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls,BeanDefinitionRegistry registry,@Nullable Object source) { Assert.notNull(registry,"BeanDefinitionRegistry must not be null"); // 判斷傳進來的類和ICO中當前存在的類哪個優先順序更高,將更高的放入IOC中 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; } //把AOP入口類封裝成beanDefinition物件,要例項化 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order",Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //註解aop入口類的beanName名稱 AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME,beanDefinition); return beanDefinition; }
首先判斷容器中是否已經存在AOP入口類,如果不存在則直接建立InfrastructureAdvisorAutoProxyCreator的BeanDefinition物件註冊到容器中,這個類也是我上一篇文章分析的AOP入口類AbstractAutoProxyCreator的子類,再來看看其繼承關係:
你會不會疑惑,這麼多子類,到底會使用哪一個呢?回到剛剛的程式碼中,可以看到如果已經存在一個入口類了,就會通過findPriorityForClass獲取兩個類的優先順序,最終就會使用優先順序更大的那個,那麼它們的優先順序順序是怎樣的呢?
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3); static { // Set up the escalation list... APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); } private static int findPriorityForClass(@Nullable String className) { // 索引即是優先順序,越大優先順序越高,IOC中只會存在一個事務AOP入口類 for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) { Class<?> clazz = APC_PRIORITY_LIST.get(i); if (clazz.getName().equals(className)) { return i; } } throw new IllegalArgumentException( "Class name [" + className + "] is not a known auto-proxy creator class"); }
可以看到,InfrastructureAdvisorAutoProxyCreator是優先順序最低的,基本上不會起作用;AspectJAwareAdvisorAutoProxyCreator是當我們配置了<aop:config>標籤時會註冊,也就是xml配置的AOP的入口類;而AnnotationAwareAspectJAutoProxyCreator是當我們配置了<aop:aspectj-autoproxy>或使用@EnableAspectJAutoProxy註解時註冊,因此大部分情況下都是使用的AnnotationAwareAspectJAutoProxyCreator。
註冊完AOP的入口類後,回到configureAutoProxyCreator方法:
RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName =parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
AnnotationTransactionAttributeSource類的作用就是封裝事務註解@Transactional的屬性,這裡需要記住其繼承體系以及熟悉該類和其父類的屬性和方法,對後面分析事物切面執行原理有幫助:
緊接著就是建立了TransactionInterceptor物件,專門的事務攔截器,並且該類是MethodInterceptor的子類,看到這個應該不陌生了,我們知道AOP呼叫鏈在執行過程中主要就是呼叫該類的invoke的方法,因此它是事務切面執行的入口。既然有了Interceptor,那麼必不可少的還應該有Advisor,而Advisor又是由Advice和Poincut組成的,這樣才能構成一個完整的切面,所以該方法後面就是建立這兩個物件。以上就是xml配置AOP註解支援的原理,很簡單,下面再來看看零配置又是如何實現的。
AOP零配置原理
使用過SpringBoot的都知道,如果需要開啟事務註解的支援,只需要一個註解就能搞定:@EnableTransactionManagement,不用再配置xml檔案,這個又是怎麼做到的呢?不多說,我們直接來看其原始碼:
@Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
在該註解下使用@Import匯入了一個類TransactionManagementConfigurationSelector,首先該註解的作用就是匯入一個類的例項到IOC容器中,你可能會說不是在類上加@Component註解就行了麼,但是有些類它並不在你掃描的路徑下,而該註解依然可以將其匯入進來,所以我麼主要看TransactionManagementConfigurationSelector類中做了些啥:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: 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); } }
可以看到在selectImports方法中返回了AutoProxyRegistrar和ProxyTransactionManagementConfiguration類,返回後會被封裝為BeanDefinition物件,那這個方法是在哪裡呼叫的呢?這個在之前的文章中也分析過,ConfigurationClassPostProcessor類中會呼叫ConfigurationClassParser類的parse方法解析@Configuration、@Import、@ImportSource等註解,具體過程這裡就不再贅述了。我們繼續來分別看看AutoProxyRegistrar和ProxyTransactionManagementConfiguration類:
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { private final Log logger = LogFactory.getLog(getClass()); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) { boolean candidateFound = false; Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata,annoType); 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,實際上這個AOP入口類起不了作用 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } } } public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { /* * 明顯是建立事務切面例項 * BeanFactoryTransactionAttributeSourceAdvisor * * */ @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { 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(); } /* * 建立事務advice * TransactionInterceptor * */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); //事務管理器要跟資料來源掛鉤,所以需要自己定義 if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
看到這就很清楚了,前者是註冊AOP的入口類(這裡註冊的入口類依然是InfrastructureAdvisorAutoProxyCreator),後者則是建立事務AOP的元件的例項到IOC中,到這裡相信不僅僅是對於事務的零配置,而是整個SpringBoot的零配置實現原理都心中有數了。
總結
本篇結合之前所學分析了事務配置解析的原理,也帶出了SpringBoot零配置實現的原理,下一篇就是事務的執行呼叫過程。我們需要在腦海將載入、解析和呼叫串聯起來,從微觀到巨集觀整體把握Spring,才能真正的理解Spring。
以上這篇這一次搞懂Spring事務註解的解析方式就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。