1. 程式人生 > 程式設計 >這一次搞懂Spring事務註解的解析方式

這一次搞懂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的子類,再來看看其繼承關係:

這一次搞懂Spring事務註解的解析方式

你會不會疑惑,這麼多子類,到底會使用哪一個呢?回到剛剛的程式碼中,可以看到如果已經存在一個入口類了,就會通過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的屬性,這裡需要記住其繼承體系以及熟悉該類和其父類的屬性和方法,對後面分析事物切面執行原理有幫助:

這一次搞懂Spring事務註解的解析方式

緊接著就是建立了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事務註解的解析方式就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。