1. 程式人生 > >【Spring Boot】(33)、SpringBoot事務管理@Transactional註解原理

【Spring Boot】(33)、SpringBoot事務管理@Transactional註解原理

1、依賴包

1.1、 SpringBoot中的依賴包

眾所周知,在SpringBoot中凡是需要跟資料庫打交道的,基本上都要顯式或者隱式新增jdbc的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

1.2、 Spring中的依賴包

在講SpringBoot中的事務管理之前,先來講下Spring的事務管理。在Spring專案中,加入的是spring-jdbc依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>

1.2.1、配置版事務

在使用配置檔案的方式中,通常會在Spring的配置檔案中配置事務管理器,並注入資料來源:

<!-- 註冊資料來源 -->
<bean id="dataSource" class="...">
	<property name="" value=""/>
</bean> <!-- 註冊事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 開啟事務註解 --> <tx:annotation-driven transaction-manager="txManager" />

接下來可以直接在業務層Service的方法上或者類上新增 @Transactional

1.2.2、註解版事務

首先需要註冊兩個Bean,分別對應上面Spring配置檔案中的兩個Bean:

@Configuration
public class TxConfig {

    @Bean
    public DataSource dataSource() {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("...");
        dataSource.setPassword("...");
        dataSource.setDriverClass("...");
        dataSource.setJdbcUrl("...");
        return dataSource;
    }
  
    @Bean
    public PlatformTransactionManager platformTransactionManager() {
		return new DataSourceTransactionManager(dataSource());//放入資料來源
    }
}

PlatformTransactionManager這個Bean非常重要,要使用事務管理,就必須要在IOC容器中註冊一個事務管理器,而在使用 @Transactional註解的時候,預設會從IOC容器中查詢對應的事務管理器。

一般人都會認為到這已經結束了,可以正常使用 @Transactional註解了,正常則commit,異常則rollback。各位可以簡單嘗試在Service層的方法中手動製造異常,看看事務是否會回滾?此處留給大家自行嘗試…

其實到這,事務在遇到異常後,還是沒有正常回滾,為什麼呢?缺少一個註解 @EnableTransactionManagement,該註解的功能是 開啟基於註解的事務管理功能,需要在配置類上新增該註解即可,這樣 @Transactional的事務提交和回滾就會生效。

@EnableTransactionManagement//重要
@Configuration
public class TxConfig {

    @Bean
    public DataSource dataSource() {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("...");
        dataSource.setPassword("...");
        dataSource.setDriverClass("...");
        dataSource.setJdbcUrl("...");
        return dataSource;
    }
  
   	//重要
    @Bean
    public PlatformTransactionManager platformTransactionManager() {
		return new DataSourceTransactionManager(dataSource());//放入資料來源
    }
}

2、@Transactional原理

這裡咱們再回到SpringBoot的事務管理。在大多數SpringBoot專案中,簡單地只要在配置類或者主類上新增 @EnableTransactionManagement,並在業務層Service上新增 @Transactional即可實現事務的提交和回滾。

因為在依賴jdbc或者jpa之後,會自動配置TransactionManager

  • 依賴jdbc會自動配置DataSourceTransactionManager
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

	@Configuration
	@ConditionalOnSingleCandidate(DataSource.class)
	static class DataSourceTransactionManagerConfiguration {

		private final DataSource dataSource;

		private final TransactionManagerCustomizers transactionManagerCustomizers;

		DataSourceTransactionManagerConfiguration(DataSource dataSource,
				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
			this.dataSource = dataSource;
			this.transactionManagerCustomizers = transactionManagerCustomizers
					.getIfAvailable();
		}

		@Bean
		@ConditionalOnMissingBean(PlatformTransactionManager.class)
		public DataSourceTransactionManager transactionManager(
				DataSourceProperties properties) {
			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
					this.dataSource);
			if (this.transactionManagerCustomizers != null) {
				this.transactionManagerCustomizers.customize(transactionManager);
			}
			return transactionManager;
		}

	}

}
  • 依賴jpa會自動配置JpaTransactionManager:
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
    
    //other code...
    
	@Bean
	@ConditionalOnMissingBean(PlatformTransactionManager.class)
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		if (this.transactionManagerCustomizers != null) {
			this.transactionManagerCustomizers.customize(transactionManager);
		}
		return transactionManager;
	}
}

2.1、@EnableTransactionManagement

具體原理還要從註解 @EnableTransactionManagement說起:

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	boolean proxyTargetClass() default false;
    
    AdviceMode mode() default AdviceMode.PROXY;
    
    int order() default Ordered.LOWEST_PRECEDENCE;
}

@EnableTransactionManagement簽名上可以看到,它匯入了TransactionManagementConfigurationSelector類,其作用就是利用該類想容器中匯入元件。而TransactionManagementConfigurationSelector主要向容器中匯入了兩個元件,分別是AutoProxyRegistrarProxyTransactionManagementConfiguration

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	/**
	 * {@inheritDoc}
	 * @return {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
	 * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}
}

2.2、AutoProxyRegistrar

咱們先來看看AutoProxyRegistrar做了哪些事?

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@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) {
                    //註冊自動代理建立器
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		//other code...
	}

}

由於預設情況下mode為AdviceMode.PROXY,所以會通過AopConfigUtils.registerAutoProxyCreatorIfNecessary方法向容器中註冊自動代理建立器:

//AopConfigUtils工具類

public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
			"org.springframework.aop.config.internalAutoProxyCreator";

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAutoProxyCreatorIfNecessary(registry, null);
}

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    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;
}

可以看到會向容器中註冊了一個名為internalAutoProxyCreator,型別為InfrastructureAdvisorAutoProxyCreator的元件Bean,而這個類其實同AnnotationAwareAspectJAutoProxyCreator類似,也是AbstractAdvisorAutoProxyCreator的實現類,而它的頂層介面類其實就是BeanPostProcessor,所以它的邏輯跟AnnotationAwareAspectJAutoProxyCreator大體上一樣:利用後置處理器機制在物件建立之後,幷包裝成代理物件,在代理物件執行目標方法的時候利用攔截器鏈進行攔截,具體過程可以參考AOP原理。

2.3、ProxyTransactionManagementConfiguration

另一個元件為ProxyTransactionManagementConfiguration

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}

	@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;
	}

}

ProxyTransactionManagementConfiguration給容器中注入了多個元件Bean,其中:

  • transactionAdvisor這個Bean用於建立一個事務增強器。
  • transactionAttributeSource這個Bean主要儲存事務屬性,其中包括用於解析不同包下的 @Transactional註解的解析器:
public AnnotationTransactionAttributeSource() {
    this(true);
}

public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2);
    this.annotationParsers.add(new SpringTransactionAnnotationParser());
    if (jta12Present) {
        this.annotationParsers.add(new JtaTransactionAnnotationParser());
    }
    if (ejb3Present) {
        this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
    }
}

其中包括Spring中的@Transactional註解,同時也包括了Jta和Ejb3中的@Transactional註解解析器。

  • transactionInterceptor這個Bean主要用於建立事務攔截器,其中設定上面所說到的事務屬性Bean,同時也儲存了事務管理器。TransactionInterceptor其實也是MethodInterceptor的子類。所以在代理物件執行目標方法的時候,方法攔截器就會進行攔截並工作。具體如何攔截工作,請繼續往下看invoke方法:
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
        @Override
        public Object proceedWithInvocation() throws Throwable {
            return invocation.proceed();
        }
    });
}

重點就在invokeWithinTransaction方法:

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
			throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
    //獲取事務註解屬性
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    //獲取事務管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        //
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, new TransactionCallback<Object>() {
 	  @Override                                                                            public Object doInTransaction(TransactionStatus status) {                               TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
          try {
	              return invocation.proceedWithInvocation();
          } catch (Throwable ex) {
              if (txAttr.rollbackOn(ex)) {
              // A RuntimeException: will lead to a rollback.
              	if (ex instanceof RuntimeException) {
                   throw (RuntimeException) ex;
                } else {
               		throw new ThrowableHolderException(ex);
                }
              }else {
                  // A normal return value: will lead to a commit.
                  return new ThrowableHolder(ex);
              }
          } finally {
              cleanupTransactionInfo(txInfo); 
          }