今天我們來看下spring 的 tx 模塊的核心流程。
1. 實例
配置一個啓動事務管理類,配置一個數據源事務管理器:
@Configuration
@EnableTransactionManagement
static class DefaultTxManagerNameConfig {
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
配置一個數據源、JdbcFooRepository 類
@Configuration
static class Config {
@Bean
FooRepository fooRepository() {
JdbcFooRepository repos = new JdbcFooRepository();
repos.setDataSource(dataSource());
return repos;
}
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.build();
}
}
基礎的 DAO 類,在 findAll() 方法上標註 @Transactional 註解
interface FooRepository {
List<Object> findAll();
}
static class JdbcFooRepository implements FooRepository {
public void setDataSource(DataSource dataSource) {
}
@Override
@Transactional
public List<Object> findAll() {
ArrayList<Object> result = new ArrayList<>();
for (int i1 = 0; i1 < 10; i1++) {
int random = (int) (Math.random() * 100);
result.add(random);
}
return result;
}
}
最後配置一個測試類:
public class EnableTransactionManagementIntegrationTests {
@Test
void repositoryIsTxProxy_withDefaultTxManagerName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class, DefaultTxManagerNameConfig.class);
FooRepository repo = ctx.getBean(FooRepository.class);
List<Object> all = repo.findAll();
System.out.println("all = " + all);
}
}
這樣就配置了一個使用事務註解支持事務的方法的例子了。
2. 分析
我們從 DefaultTxManagerNameConfig 上的 @EnableTransactionManagement 開始看:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
/**
* 指定使用什麼代理模式(true 爲 cglib 代理,false 爲 jdk 代理)
*
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
* opposed to standard Java interface-based proxies ({@code false}). The default is
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
* {@link AdviceMode#PROXY}</strong>.
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Transactional}. For example, other beans marked with Spring's
* {@code @Async} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*/
boolean proxyTargetClass() default false;
/**
* 通知的模式,代理模式或者 aspectj,一般是使用代理模式。
* 注意代理模式只允許調用攔截,通過在本類中的本地調用不能被攔截;
* 一個 Transactional 註解在一個本地調用的方法上將會被 spring 的攔截器忽略,甚至不會再這種場景中出現。
* 對於攔截更多高級
*
* Indicate how transactional advice should be applied.
* <p><b>The default is {@link AdviceMode#PROXY}.</b>
* Please note that proxy mode allows for interception of calls through the proxy
* only. Local calls within the same class cannot get intercepted that way; an
* {@link Transactional} annotation on such a method within a local call will be
* ignored since Spring's interceptor does not even kick in for such a runtime
* scenario. For a more advanced mode of interception, consider switching this to
* {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the transaction advisor
* when multiple advices are applied at a specific joinpoint.
* <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
這個類有三個屬性,proxyTargetClass、mode、order,分別表示:是否使用 cglib 代理、通知的模式(PROXY 或者 ASPECTJ)、排序。
它還使用了 @Import(TransactionManagementConfigurationSelector.class) 註解,導入了 TransactionManagementConfigurationSelector 類:
/**
* 在導入 @Configuration 註解類時候,根據選擇 EnableTransactionManagement 的 mode,來選擇
* AbstractTransactionManagementConfiguration 類合適的子類,
*
* Selects which implementation of {@link AbstractTransactionManagementConfiguration}
* should be used based on the value of {@link EnableTransactionManagement#mode} on the
* importing {@code @Configuration} class.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see EnableTransactionManagement
* @see ProxyTransactionManagementConfiguration
* @see TransactionManagementConfigUtils#TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
* @see TransactionManagementConfigUtils#JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
*/
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* Returns {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJ(Jta)TransactionManagementConfiguration} 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[] {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);
}
}
看下它的類圖結構:
這個類繼承了 AdviceModeImportSelector 類,它是一個 ImportSelector 導入選擇器,重寫了 selectImports() 方法,通過 adviceMode 來返回一組類的名稱,我們一般使用 AdviceMode.PROXY,可以看到它會返回 AutoProxyRegistrar.classs 和 ProxyTransactionManagementConfiguration.class 這兩個類的名稱。
我們回顧下 ImportSelector 類,它的 selectImports() 方法是在 org.springframework.context.annotation.ConfigurationClassParser#processImports 中進行調用的。它是在 spring 容器啓動時,執行 bean 工廠註冊器後置處理器時,調用了 ConfigurationClassParser 配置類解析器的解析掃描 @Configuration 註解的類的 bean 定義流程中執行的。
2.1 AutoProxyRegistrar
接着繼續看 AutoProxyRegistrar.classs 這個類,它的類圖:
它實現爲:
/**
* 針對當前的 BeanDefinitionRegistry 作爲一個合適的基於 @Enable* 註解標有 mode 和 proxyTargetClass 的屬性,
* 設置到正確的值。
*
* Registers an auto proxy creator against the current {@link BeanDefinitionRegistry}
* as appropriate based on an {@code @Enable*} annotation having {@code mode} and
* {@code proxyTargetClass} attributes set to the correct values.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.cache.annotation.EnableCaching
* @see org.springframework.transaction.annotation.EnableTransactionManagement
*/
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory.getLog(getClass());
/**
* 註冊bean 定義
*
* Register, escalate, and configure the standard auto proxy creator (APC) against the
* given registry. Works by finding the nearest annotation declared on the importing
* {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
* attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
* {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
* subclass (CGLIB) proxying.
* <p>Several {@code @Enable*} annotations expose both {@code mode} and
* {@code proxyTargetClass} attributes. It is important to note that most of these
* capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
* single APC}. For this reason, this implementation doesn't "care" exactly which
* annotation it finds -- as long as it exposes the right {@code mode} and
* {@code proxyTargetClass} attributes, the APC can be registered and configured all
* the same.
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
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) {
// 強制自動代理創建器使用類代理,proxyTargetClass
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isInfoEnabled()) {
String name = getClass().getSimpleName();
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));
}
}
}
它是一個 ImportBeanDefinitionRegistrar 類型,導入 bean 定義註冊器,實現了 registerBeanDefinitions() 方法,這個方法做的事情:
- 獲取導入類的註解元數據;
- 獲取 mode 註解屬性、proxyTargetClass 註解屬性;
- 根據它們的值,註冊一個名稱爲 org.springframework.aop.config.internalAutoProxyCreator,值爲 InfrastructureAdvisorAutoProxyCreator 類型的 bean 定義;
- 以及bean 定義的 proxyTargetClass 屬性。
它的這個方法是在 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars 處調用的,也是屬於配置類後置處理器註冊已經將 bean 定義解析爲配置類的流程中。
2.2 InfrastructureAdvisorAutoProxyCreator
看下 InfrastructureAdvisorAutoProxyCreator 類,它的類圖:
它的實現:
/**
* 自動代理創建器,僅僅考慮基礎的增強器 beans,忽略其他應用程序定義的增強器
*
* Auto-proxy creator that considers infrastructure Advisor beans only,
* ignoring any application-defined Advisors.
*
* @author Juergen Hoeller
* @since 2.0.7
*/
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
this.beanFactory = beanFactory;
}
@Override
protected boolean isEligibleAdvisorBean(String beanName) {
// 判斷是否是一個合格的增強器,@Role(BeanDefinition.ROLE_INFRASTRUCTURE) bean 定義的角色是基礎類
return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
這個類它和 spring aop 中的 AnnotationAwareAspectJAutoProxyCreator 類很像,沒錯!它們都是共同繼承了 AbstractAdvisorAutoProxyCreator 類型,間接的實現了 BeanPostProcessor 的 postProcessBeforeInstantiation() 方法和 postProcessAfterInitialization() 方法。這兩個方法主要是對代理對象進行檢查以及初始化,並且進行創建其代理。
2.3 ProxyTransactionManagementConfiguration
再看下 ProxyTransactionManagementConfiguration 類:
/**
* 這是一個 @Configuration 類,它註冊了 spring 基礎類,這些類時啓動基於代理的註解驅動的事務管理器的必要類。
*
* {@code @Configuration} class that registers the Spring infrastructure beans
* necessary to enable proxy-based annotation-driven transaction management.
*
* @author Chris Beams
* @author Sebastien Deleuze
* @since 3.1
* @see EnableTransactionManagement
* @see TransactionManagementConfigurationSelector
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
/**
* 註冊一個內部的事務增強器 org.springframework.transaction.config.internalTransactionAdvisor
*
* @param transactionAttributeSource
* @param transactionInterceptor
* @return
*/
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
// 設置事務屬性源
advisor.setTransactionAttributeSource(transactionAttributeSource);
// 設置通知事務攔截器
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
/**
* 定義一個註解事務屬性源
*
* @return
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// 註解的事務屬性源
return new AnnotationTransactionAttributeSource();
}
/**
* 定義一個事務攔截器
*
* @param transactionAttributeSource
* @return
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
// 設置事務屬性源
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
// 設置事務管理器
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
這個類引入了三個 bean:AnnotationTransactionAttributeSource、TransactionInterceptor、BeanFactoryTransactionAttributeSourceAdvisor。
2.4 AnnotationTransactionAttributeSource
它是一個註解事務屬性源,看下它的類圖:
它的實現:
/**
* Implementation of the
* {@link org.springframework.transaction.interceptor.TransactionAttributeSource}
* interface for working with transaction metadata in JDK 1.5+ annotation format.
*
* <p>This class reads Spring's JDK 1.5+ {@link Transactional} annotation and
* exposes corresponding transaction attributes to Spring's transaction infrastructure.
* Also supports JTA 1.2's {@link javax.transaction.Transactional} and EJB3's
* {@link javax.ejb.TransactionAttribute} annotation (if present).
* This class may also serve as base class for a custom TransactionAttributeSource,
* or get customized through {@link TransactionAnnotationParser} strategies.
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @since 1.2
* @see Transactional
* @see TransactionAnnotationParser
* @see SpringTransactionAnnotationParser
* @see Ejb3TransactionAnnotationParser
* @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource
*/
@SuppressWarnings("serial")
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
implements Serializable {
private static final boolean jta12Present;
private static final boolean ejb3Present;
static {
ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
}
private final boolean publicMethodsOnly;
private final Set<TransactionAnnotationParser> annotationParsers;
/**
* Create a default AnnotationTransactionAttributeSource, supporting
* public methods that carry the {@code Transactional} annotation
* or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
*/
public AnnotationTransactionAttributeSource() {
this(true);
}
/**
* Create a custom AnnotationTransactionAttributeSource, supporting
* public methods that carry the {@code Transactional} annotation
* or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
* @param publicMethodsOnly whether to support public methods that carry
* the {@code Transactional} annotation only (typically for use
* with proxy-based AOP), or protected/private methods as well
* (typically used with AspectJ class weaving)
*/
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
if (jta12Present || ejb3Present) {
this.annotationParsers = new LinkedHashSet<>(4);
this.annotationParsers.add(new SpringTransactionAnnotationParser());
if (jta12Present) {
this.annotationParsers.add(new JtaTransactionAnnotationParser());
}
if (ejb3Present) {
this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
}
}
else {
this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
}
}
/**
* Create a custom AnnotationTransactionAttributeSource.
* @param annotationParser the TransactionAnnotationParser to use
*/
public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) {
this.publicMethodsOnly = true;
Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null");
this.annotationParsers = Collections.singleton(annotationParser);
}
/**
* Create a custom AnnotationTransactionAttributeSource.
* @param annotationParsers the TransactionAnnotationParsers to use
*/
public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
this.publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
}
/**
* Create a custom AnnotationTransactionAttributeSource.
* @param annotationParsers the TransactionAnnotationParsers to use
*/
public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) {
this.publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
this.annotationParsers = annotationParsers;
}
@Override
public boolean isCandidateClass(Class<?> targetClass) {
for (TransactionAnnotationParser parser : this.annotationParsers) {
if (parser.isCandidateClass(targetClass)) {
return true;
}
}
return false;
}
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
// 從類上找事務屬性
return determineTransactionAttribute(clazz);
}
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Method method) {
// 從方法上找事務屬性
return determineTransactionAttribute(method);
}
/**
* Determine the transaction attribute for the given method or class.
* <p>This implementation delegates to configured
* {@link TransactionAnnotationParser TransactionAnnotationParsers}
* for parsing known annotations into Spring's metadata attribute class.
* Returns {@code null} if it's not transactional.
* <p>Can be overridden to support custom annotations that carry transaction metadata.
* @param element the annotated method or class
* @return the configured transaction attribute, or {@code null} if none was found
*/
@Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
// 遍歷所有的註解解析器
for (TransactionAnnotationParser parser : this.annotationParsers) {
// 從事務註解解析器上解析
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
/**
* By default, only public methods can be made transactional.
*/
@Override
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationTransactionAttributeSource)) {
return false;
}
AnnotationTransactionAttributeSource otherTas = (AnnotationTransactionAttributeSource) other;
return (this.annotationParsers.equals(otherTas.annotationParsers) &&
this.publicMethodsOnly == otherTas.publicMethodsOnly);
}
@Override
public int hashCode() {
return this.annotationParsers.hashCode();
}
}
這個類間接的實現了 TransactionAttributeSource 接口,間接的實現了它的 org.springframework.transaction.interceptor.TransactionAttributeSource#isCandidateClass 和 org.springframework.transaction.interceptor.TransactionAttributeSource#getTransactionAttribute 方法,這兩個方法都是在 org.springframework.aop.support.AopUtils#canApply 這個方法中被調用的,AopUtils#canApply 方法又是在上面提到的實現了 BeanPostProcessor 接口的 postProcessAfterInitialization() 方法的 InfrastructureAdvisorAutoProxyCreator 類所實現。
2.4.1 判斷是否候選類 isCandidateClass()
isCandidateClass() 方法:它在 bean 初始化之後,在獲取通知和增強器的方法邏輯 getAdvicesAndAdvisorsForBean() 中調用 findAdvisorsThatCanApply(),再調用 canApply() 方法,根據 BeanFactoryTransactionAttributeSourceAdvisor 增強器,獲取事務屬性源切點 BeanFactoryTransactionAttributeSourceAdvisor#pointcut TransactionAttributeSourcePointcut 類型,獲取切點 AnnotationTransactionAttributeSource 類,間接的調用它的 isCandidateClass() 方法,最後調用 SpringTransactionAnnotationParser 的 isCandidateClass() 方法,由 AnnotationUtils 工具類判斷目標類是否有 @Transactional 來判斷是否符合合格的類;
2.4.2 獲取事務屬性 TransactionAttribute 類型 getTransactionAttribute()
它實現 org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute:
/**
* 爲這個方法執行獲取事務屬性。如果方法屬性沒有找到,默認是爲類的事務屬性。
*
* Determine the transaction attribute for this method invocation.
* <p>Defaults to the class's transaction attribute if no method attribute is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @return a TransactionAttribute for this method, or {@code null} if the method
* is not transactional
*/
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 首先,從緩存中找
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
}
else {
// 計算事務屬性
// We need to work it out.
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
// 在緩存中標識事務屬性爲空
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
// 爲事務屬性設置方法描述符
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute) {
DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;
dta.setDescriptor(methodIdentification);
dta.resolveAttributeStrings(this.embeddedValueResolver);
}
if (logger.isTraceEnabled()) {
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
// 加入到緩存
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
/**
* Determine a cache key for the given method and target class.
* <p>Must not produce same key for overloaded methods.
* Must produce same key for different instances of the same method.
* @param method the method (never {@code null})
* @param targetClass the target class (may be {@code null})
* @return the cache key (never {@code null})
*/
protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
return new MethodClassKey(method, targetClass);
}
/**
* 計算屬性源
*
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* <p>As of 4.1.8, this method can be overridden.
* @since 4.1.8
* @see #getTransactionAttribute
*/
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 不允許非 public 修飾的方法
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 方法可能在接口上,但是我們需要從目標類上獲取屬性。
// 如果目標類是空,該方法將保持不變
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 首先嚐試的是目標類上的方法
// First try is the method in the target class.
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// 然後嘗試的是在方法所在的類上
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// 從方法上找
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// 最後在方法所在的接口上找
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
getTransactionAttribute() 方法:也是在上述 isCandidateClass() 方法調用邏輯中的 org.springframework.aop.support.AopUtils#canApply 方法中,通過 TransactionAttributeSourcePointcut 類調用 matches() 方法,再調用 getTransactionAttributeSource() 方法獲取事務屬性源,最後獲取事務屬性 TransactionAttribute。它主要是在 org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute 這個方法中實現的邏輯:
- 先從緩存中查找事務屬性;
- 計算事務屬性 computeTransactionAttribute();
- 判斷方法是否是 public 修飾的;
- 獲取目標方法;
- 先從目標類上的方法查找 @Transcation 註解;
- 然後嘗試從目標方法所在類上查找 @Transcation 註解;
- 在從接口的方法上查找 @Transcation 註解;
- 最後再從接口類上的查找 @Transcation 註解;
- 保存到緩存。
2.4.3 查找事務屬性 findTransactionAttribute()
這個方法是一個重載方法,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.Class<?>)
和 org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.reflect.Method)
,最終都調用到了 org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#determineTransactionAttribute 方法:
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
// 遍歷所有的註解解析器
for (TransactionAnnotationParser parser : this.annotationParsers) {
// 從事務註解解析器上解析
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
它通過事務註解解析器來解析:org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
// 解析 @Transactional 註解
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
// 真正的開始解析
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
/**
* 開始解析事務註解
*
* @param attributes
* @return
*/
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
// 規則的事務屬性
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 傳播行爲
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
// 隔離級別
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
// 超時時間
rbta.setTimeout(attributes.getNumber("timeout").intValue());
String timeoutString = attributes.getString("timeoutString");
Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
"Specify 'timeout' or 'timeoutString', not both");
rbta.setTimeoutString(timeoutString);
// 是否只讀
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 事務名稱
rbta.setQualifier(attributes.getString("value"));
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
// 回滾規則
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 對哪個類進行回滾
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 對哪些異常不回滾
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
// 對哪些類不回滾
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
可以看到它最終會返回一個 RuleBasedTransactionAttribute 類型的事務屬性。
2.4.4 事務定義 RuleBasedTransactionAttribute
它的類圖:
這個類實現了 TransactionDefinition 接口,我們看下這個接口類圖是:
這個類主要定義了事務的一些基本屬性信息,有事務名稱、事務傳播行爲、事務隔離級別、超時時間、是否只讀。
2.5 BeanFactoryTransactionAttributeSourceAdvisor
這個類的類圖:
它的實現:
/**
* bean 工廠事務屬性源增強器
*
* Advisor driven by a {@link TransactionAttributeSource}, used to include
* a transaction advice bean for methods that are transactional.
*
* @author Juergen Hoeller
* @since 2.5.5
* @see #setAdviceBeanName
* @see TransactionInterceptor
* @see TransactionAttributeSourceAdvisor
*/
@SuppressWarnings("serial")
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private TransactionAttributeSource transactionAttributeSource;
/**
* 事務屬性源切點
*/
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
@Nullable
protected TransactionAttributeSource getTransactionAttributeSource() {
// 獲取事務屬性源 AnnotationTransactionAttributeSource 類型
return transactionAttributeSource;
}
};
/**
* 通過注入
* Set the transaction attribute source which is used to find transaction
* attributes. This should usually be identical to the source reference
* set on the transaction interceptor itself.
* @see TransactionInterceptor#setTransactionAttributeSource
*/
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionAttributeSource = transactionAttributeSource;
}
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter) {
this.pointcut.setClassFilter(classFilter);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
它是一個 PointcutAdvisor 類型實現類,切點增強器,它也是實現 getPointcut() 方法,返回一個 TransactionAttributeSourcePointcut 類,這個類在上面的 canApply() 方法中用到了。
2.6 TransactionInterceptor
這個類的類圖:
再看下它的實現:
@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
/**
* Create a new TransactionInterceptor.
* <p>Transaction manager and transaction attributes still need to be set.
* @see #setTransactionManager
* @see #setTransactionAttributes(java.util.Properties)
* @see #setTransactionAttributeSource(TransactionAttributeSource)
*/
public TransactionInterceptor() {
}
/**
* Create a new TransactionInterceptor.
* @param ptm the default transaction manager to perform the actual transaction management
* @param tas the attribute source to be used to find transaction attributes
* @since 5.2.5
* @see #setTransactionManager
* @see #setTransactionAttributeSource
*/
public TransactionInterceptor(TransactionManager ptm, TransactionAttributeSource tas) {
setTransactionManager(ptm);
setTransactionAttributeSource(tas);
}
/**
* Create a new TransactionInterceptor.
* @param ptm the default transaction manager to perform the actual transaction management
* @param tas the attribute source to be used to find transaction attributes
* @see #setTransactionManager
* @see #setTransactionAttributeSource
* @deprecated as of 5.2.5, in favor of
* {@link #TransactionInterceptor(TransactionManager, TransactionAttributeSource)}
*/
@Deprecated
public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
setTransactionManager(ptm);
setTransactionAttributeSource(tas);
}
/**
* Create a new TransactionInterceptor.
* @param ptm the default transaction manager to perform the actual transaction management
* @param attributes the transaction attributes in properties format
* @see #setTransactionManager
* @see #setTransactionAttributes(java.util.Properties)
* @deprecated as of 5.2.5, in favor of {@link #setTransactionAttributes(Properties)}
*/
@Deprecated
public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
setTransactionManager(ptm);
setTransactionAttributes(attributes);
}
/**
* 事務攔截器進行攔截調用
*
* @param invocation the method invocation joinpoint
* @return
* @throws Throwable
*/
@Override
@Nullable
public Object invoke(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, invocation::proceed);
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManagerBeanName());
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
oos.writeObject(getBeanFactory());
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
ois.defaultReadObject();
// Serialize all relevant superclass fields.
// Superclass can't implement Serializable because it also serves as base class
// for AspectJ aspects (which are not allowed to implement Serializable)!
setTransactionManagerBeanName((String) ois.readObject());
setTransactionManager((PlatformTransactionManager) ois.readObject());
setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
setBeanFactory((BeanFactory) ois.readObject());
}
}
TransactionInterceptor 是一個實現了 MethodInterceptor 接口的 invoke() 方法,對方法執行進行攔截,間接調用了 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 方法:
/**
* 用於基於環繞通知子類的總代表,委派到該類的其他幾個模板方法。能夠處理 CallbackPreferringPlatformTransactionManager
* 和常規的 PlatformTransactionManager 實現類以及 ReactiveTransactionManager 實現類,對於無返回類型。
*
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations and
* {@link ReactiveTransactionManager} implementations for reactive return types.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 如果事務屬性爲空,那麼這個方法就是非事務方法
// If the transaction attribute is null, the method is non-transactional.
// 獲取事務屬性源
TransactionAttributeSource tas = getTransactionAttributeSource();
// 獲取事務屬性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 獲取項目中事務管理器,一般是 DataSourceTransactionManager 類
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 獲取我們需要切入的方法(也就是我們標識了 @Transactional 註解的方法)
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 事務信息
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
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);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事務
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
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.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
梳理下這個方法的核心流程:
- 獲取事務屬性源 TransactionAttributeSource;
- 獲取事務屬性 TransactionAttribute,這裏會返回 RuleBasedTransactionAttribute 類型;
- 獲取事務管理器 TransactionManager;
- 獲取方法描述器 joinpointIdentification;
- 執行 createTransactionIfNecessary() 方法,創建事務信息 TransactionInfo;
- 執行 invocation.proceedWithInvocation() 執行器方法,這將會執行調用鏈中下一個攔截器,如果沒有攔截,則執行目標方法;
- 遇到異常之後執行 completeTransactionAfterThrowing() 方法,進行回滾或提交操作,然後拋出異常;
- 執行 cleanupTransactionInfo() 方法,清除事務信息;
- 沒有異常的情況,執行 commitTransactionAfterReturning() 提交事務;
- 返回執行結果。
2.6.1 創建事務信息 createTransactionIfNecessary()
看下 org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary 這個創建事務信息方法:
/**
* 根據給定的事務屬性來創建一個事務。
*
* Create a transaction if necessary based on the given TransactionAttribute.
* <p>Allows callers to perform custom TransactionAttribute lookups through
* the TransactionAttributeSource.
* @param txAttr the TransactionAttribute (may be {@code null})
* @param joinpointIdentification the fully qualified method name
* (used for monitoring and logging purposes)
* @return a TransactionInfo object, whether or not a transaction was created.
* The {@code hasTransaction()} method on TransactionInfo can be used to
* tell if there was a transaction created.
* @see #getTransactionAttributeSource()
*/
@SuppressWarnings("serial")
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 包裝事務屬性,名稱爲方法描述符
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 獲取一個事務狀態
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 獲取事務,返回事務狀態
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 準備事務信息
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
它的邏輯:
- 通過事務管理器獲取事務狀態 getTransaction();
- 準備事務信息 prepareTransactionInfo(),創建事務信息 TransactionInfo 類;
2.6.1.1 獲取事務狀態 getTransaction()
看下如何獲取事務狀態信息:org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
/**
* 實現處理傳播行爲的方法,委派到 doGetTransaction、isExistingTransaction、doBegin 方法
*
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*/
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// 獲取事務定義
// Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 獲取事務對象
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 判斷是否存在事務
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 處理已存在的事務
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 判斷事務超時
// Check definition settings for new transaction.
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 判斷事務傳播行爲 MANDATORY 必須的
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 掛起事務,但是當前沒有事務
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 開始一個新的事務
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 重新使用資源
resume(null, suspendedResources);
throw ex;
}
}
else {
// 創建一個空的事務,但是它會潛在的同步
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
它的邏輯:
- 執行 doGetTransaction() 方法,獲取事務對象,返回一個 DataSourceTransactionObject 類型對象;
- 判斷是否存在事務 isExistingTransaction();
- 如果存在事務,執行 handleExistingTransaction() 方法,處理已存在的事務,結束;
- 判斷事務是否超時,判斷正確的傳播行爲;
- 判斷傳播行爲如果是支持當前事務的(REQUIRED、REQUIRED_NEW、NESTED),先執行 suspend() 方法,掛起當前事務,然後執行 startTransaction() 開啓一個新事物,結束;
- 否則執行 prepareTransactionStatus() 方法,創建一個空的事務,結束。
2.6.1.2 獲取 DataSourceTransactionObject 事務對象 doGetTransaction()
看下它的實現 org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction:
/**
* 獲取事務對象
*
* @return
*/
@Override
protected Object doGetTransaction() {
// 創建一個數據源事務對象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 設置允許嵌套事務(保存點)
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 獲取連接持有器
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 爲數據源事務對象設置連接持有器(首次獲取爲空)
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
可以看到事務對象是一個 DataSourceTransactionObject 類型的對象,它的類圖結構:
它擁有的屬性有:連接持有器、是否允許保存點、是否只讀、前一個事務隔離級別、是否新的連接持有器、是否僅支持回滾、是否必須還原自動提交屬性等等。
上面的 doGetTransaction() 方法邏輯:
- 創建一個 DataSourceTransactionObject 類型對象;
- 設置是否允許保存點(即是否允許嵌套事務);
- 獲取連接持有器(這裏是從線程本地化中獲取的,首次獲取爲 null);
- 爲事務對象設置連接持有器。
2.6.1.3 判斷是否存在事務 isExistingTransaction()
我們先看下它是如何判斷事務是否存在的:org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction
/**
* 判斷是否存在事務
*
* @param transaction the transaction object returned by doGetTransaction
* @return
*/
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 事務有連接持有器 && 連接持有器中的事務是活躍的
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
- 事務對象是否有連接持有器;
- 連接持有器中的事務是否是活躍的。
2.6.1.4 處理已存在的事務 handleExistingTransaction()
接着看處理已經存在的事務 org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction 方法:
/**
* 爲一個已經存在的事務,創建一個事務狀態
*
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 判斷傳播行爲:never 拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 傳播行爲是 NOT_SUPPORTED 不執行事務
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 掛起當前事務
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 準備事務狀態
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 傳播行爲:REQUIRES_NEW 開啓一個新的事務
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
// 掛起當前事務
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 開啓一個新的事務
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 傳播行爲:NESTED 嵌套事務
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 是否爲嵌套事務保存點
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
// 創建一個新的事務狀態
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 創建和持有保存點
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 開啓事務
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
// 驗證現有的事務
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
// 傳播行爲:不是 NEVER,準備事務狀態
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
邏輯:
- 判斷傳播行爲,不同的傳播行爲執行不同的邏輯;
- 傳播行爲是 NOT_SUPPORTED 不執行事務;
- 先執行 suspend() 掛起當前事務;
- 執行 prepareTransactionStatus() 準備事務。
- 傳播行爲是 REQUIRES_NEW 開啓新事物;
- 先執行 suspend() 掛起當前事務;
- 執行 startTransaction() 開啓一個新事物。
- 傳播行爲是 NESTED 嵌套事務;
- 判斷是否爲嵌套事務使用保存點;
- 如果不是,則執行 startTransaction() 方法,開啓一個新事物;
- 如果是,則先執行 prepareTransactionStatus() 創建一個新的事務狀態 DefaultTransactionStatus,然後執行事務狀態的 createAndHoldSavepoint() 創建保存點,返回。
- 驗證現有的事務;
- 其他的傳播行爲,執行 prepareTransactionStatus() 方法,準備事務狀態。
2.6.1.5 掛起事務 suspend()
掛起事務是 org.springframework.transaction.support.AbstractPlatformTransactionManager#suspend 方法中實現的:
/**
* 掛起給定的事務。首先掛起事務同步,然後委派 doSuspend 模板方法。
*
* Suspend the given transaction. Suspends transaction synchronization first,
* then delegates to the {@code doSuspend} template method.
* @param transaction the current transaction object
* (or {@code null} to just suspend active synchronizations, if any)
* @return an object that holds suspended resources
* (or {@code null} if neither transaction nor synchronization active)
* @see #doSuspend
* @see #resume
*/
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 判斷是否同步是活躍的
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 執行掛起同步
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// 對給定的事務進行掛起,返回一個當前連接資源
suspendedResources = doSuspend(transaction);
}
// 清除當前事務信息
// 當前事務名稱
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
// 是否只讀
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
// 隔離級別
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
// 是哦福活躍
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
// 創建新一個掛起資源持有器,把當前事務信息都保存起來
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
/**
* 掛起所有的當前同步,並且取消激活當前線程的事務同步
* Suspend all current synchronizations and deactivate transaction
* synchronization for the current thread.
* @return the List of suspended TransactionSynchronization objects
*/
private List<TransactionSynchronization> doSuspendSynchronization() {
// 獲取當前線程中全部的事務同步
List<TransactionSynchronization> suspendedSynchronizations =
TransactionSynchronizationManager.getSynchronizations();
// 遍歷同步,使其依次掛起
for (TransactionSynchronization synchronization : suspendedSynchronizations) {
// 就是釋放數據庫連接資源
synchronization.suspend();
}
// 清理同步
TransactionSynchronizationManager.clearSynchronization();
// 返回同步
return suspendedSynchronizations;
}
======================================= org.springframework.jdbc.datasource.DataSourceTransactionManager#doSuspend =======================================
/**
* 執行掛起事務
*
* @param transaction the transaction object returned by {@code doGetTransaction}
* @return
*/
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
// 釋放資源
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
它的邏輯是:
- 執行掛起同步 doSuspendSynchronization() 方法,這裏邊主要是從線程本地化中獲取所有的事務同步 TransactionSynchronization,然後執行掛起(就是釋放數據庫連接資源);
- 然後執行 doSuspend() 方法,掛起給定的事務,返回一個當前連接資源;
- 清除線程本地化中當前的事務信息,有線程名稱、線程只讀屬性、線程隔離級別、事務是否活躍標識;
- 創建一個掛起的資源持有器 SuspendedResourcesHolder,返回。
- 遇到異常執行 doResumeSynchronization() 方法,重新使用同步。
2.6.1.6 準備事務狀態 prepareTransactionStatus()
接着看準備事務狀態方法 org.springframework.transaction.support.AbstractPlatformTransactionManager#prepareTransactionStatus :
/**
* 創建一個新的事務狀態,同時初始化事務同步。
*
* Create a new TransactionStatus for the given arguments,
* also initializing transaction synchronization as appropriate.
* @see #newTransactionStatus
* @see #prepareTransactionStatus
*/
protected final DefaultTransactionStatus prepareTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
// 創建一個新的事務狀態
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
// 預同步事務
prepareSynchronization(status, definition);
return status;
}
/**
* Create a TransactionStatus instance for the given arguments.
*/
protected DefaultTransactionStatus newTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
// 創建一個默認的事務狀態
return new DefaultTransactionStatus(
transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}
/**
* Initialize transaction synchronization as appropriate.
*/
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
// 判斷是否爲新的同步
if (status.isNewSynchronization()) {
// 設置實際事務活躍
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
// 設置當前事務隔離級別
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
// 設置當前事務是否爲只讀
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
// 設置當前事務的名稱
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
// 初始化同步
TransactionSynchronizationManager.initSynchronization();
}
}
邏輯:
- 執行 newTransactionStatus() 方法,創建一個事務狀態 DefaultTransactionStatus 對象;
- 執行 prepareSynchronization() 方法,準備同步。
- 判斷事務狀態如果是新的同步,則執行以下邏輯;
- 設置線程本地化的實際事務活躍;
- 設置線程本地化的當前事務隔離級別;
- 設置線程本地化的當前事務是否爲只讀標識;
- 設置當前事務的名稱;
- 初始化線程本地化同步集合。
2.6.1.7 事務狀態 DefaultTransactionStatus
我們看下 DefaultTransactionStatus 對象的類圖:
這個類有持有了一些事務的屬性,表示當前事務的一個狀態。
2.6.1.8 開啓一個新事物 startTransaction()
開始看開啓新事物方法 org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction :
/**
* 開啓一個新的事務
*
* Start a new transaction.
*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創建一個事務狀態
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啓事務
doBegin(transaction, definition);
// 準備同步事務
prepareSynchronization(status, definition);
return status;
}
================================ org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin ================================
/**
* 開啓一個新的事務
*
* @param transaction the transaction object returned by {@code doGetTransaction}
* @param definition a TransactionDefinition instance, describing propagation
* behavior, isolation level, read-only flag, timeout, and transaction name
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 判斷是否有連接持有器
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 獲取一個新的連接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 創建一個新的連接持有器,設置到事務對象中
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 設置連接持有器的事務同步屬性
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 獲取連接對象
con = txObject.getConnectionHolder().getConnection();
// 預設值連接屬性,返回隔離級別
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 爲事務對象設置隔離級別
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 設置只讀屬性
txObject.setReadOnly(definition.isReadOnly());
// 獲取自定提交的屬性,設置爲 false
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 預處理事務連接
prepareTransactionalConnection(con, definition);
// 設置事務對象的連接持有器的活躍屬性
txObject.getConnectionHolder().setTransactionActive(true);
// 超時時間
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 綁定一個資源
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
// 遇到異常,釋放資源
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
// 清空連接持有器
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
它的邏輯:
- 執行 newTransactionStatus() 方法,創建一個事務狀態 DefaultTransactionStatus 對象;
- 執行 doBegin() 方法,開啓事務;
- 判斷事務對象 DataSourceTransactionObject 是否有連接持有器;
- 如果沒有,創建一個新的連接 Connection、連接持有器 ConnectionHolder,並設置到事務對象中;
- 設置連接持有器的同步標識;
- 獲取連接,爲事務準備連接,
- 設置事務對象只讀標識屬性、隔離級別屬性;
- 關閉連接的自動提交;
- 準備事務連接;
- 設置連接持有器的事務激活標識爲 true;
- 設置事務對象的超時時間;
- 持有器是新的話,就綁定一個連接資源,到線程本地化中。
- 遇到異常,持有器是新的話,就釋放資源,清空事務對象的連接持有器。
- 執行 prepareSynchronization() 方法,準備同步。
2.6.1.9 準備事務信息 prepareTransactionInfo()
獲取了一個事務狀態之後,接着到了準備事務信息方法了,org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo:
/**
* 準備一個事務信息。
*
* Prepare a TransactionInfo for the given attribute and status object.
* @param txAttr the TransactionAttribute (may be {@code null})
* @param joinpointIdentification the fully qualified method name
* (used for monitoring and logging purposes)
* @param status the TransactionStatus for the current transaction
* @return the prepared TransactionInfo object
*/
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
// 創建一個事務信息
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 設置事務狀態
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
}
else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
// We always bind the TransactionInfo to the thread, even if we didn't create
// a new transaction here. This guarantees that the TransactionInfo stack
// will be managed correctly even if no transaction was created by this aspect.
// 綁定線程
txInfo.bindToThread();
return txInfo;
}
邏輯:
- 創建一個事務信息 TransactionInfo 對象;
- 設置事務信息的事務狀態屬性;
- 把事務信息綁定到綁定線程,保存舊的事務信息,把當前事務信息綁定到線程本地化中;
- 返回事務信息。
我們看下這個 TransactionInfo 類信息:
它持有一個事務狀態、事務管理器、事務屬性、方法切點。
2.6.2 處理異常 completeTransactionAfterThrowing()
在執行器執行過程中遇到異常時,會執行 org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing 方法:
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 判斷是否對該異常進行回滾
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 進行回滾
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// 我們不會回滾這個異常。
// 如果 TransactionStatus.isRollbackOnly() 是 true,則任然回滾
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
邏輯爲:
- 判斷是否對該異常進行回滾;
- 如果需要,則進行回滾;
- 如果不需要則執行提交事務。
2.6.3 清理事務信息 cleanupTransactionInfo()
當目標方法都處理完畢之後,不論有沒有異常拋出,都進行清除事務信息 org.springframework.transaction.interceptor.TransactionAspectSupport#cleanupTransactionInfo:
/**
* Reset the TransactionInfo ThreadLocal.
* <p>Call this in all cases: exception or normal return!
* @param txInfo information about the current transaction (may be {@code null})
*/
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
// 恢復線程本地化狀態
txInfo.restoreThreadLocalStatus();
}
}
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
// 還原上一個事務信息
transactionInfoHolder.set(this.oldTransactionInfo);
}
主要是還原上一個事務信息到線程本地中。
2.6.4 提交事務 commitTransactionAfterReturning()
最後,當方法執行完畢,沒有異常的情況下,會執行提交事務操作 org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning:
/**
* Execute after successful completion of call, but not after an exception was handled.
* Do nothing if we didn't create a transaction.
* @param txInfo information about the current transaction
*/
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 提交事務
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
======================== org.springframework.transaction.support.AbstractPlatformTransactionManager#commit ====================
/**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to {@code isRollbackOnly}, {@code doCommit}
* and {@code rollback}.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 事務狀態是僅回滾的,執行回滾
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 執行提交
processCommit(defStatus);
}
/**
* 處理實際的回滾。已經檢查了完成標記。
*
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 觸發前置完成事件
triggerBeforeCompletion(status);
// 判斷保存點
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
// 回滾到保存點
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// 新的事務,進行回滾
doRollback(status);
}
else {
// Participating in larger transaction
// 判斷是否有事務
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 設置僅回滾屬性
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
// 觸發完成之後事件
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 觸發完成之後事件
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 完成之後的清理事件
cleanupAfterCompletion(status);
}
}
/**
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 釋放保存點
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 執行提交
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
這裏的邏輯稍微複雜點:
- 判斷事務狀態是否是僅回滾的,如果是那就執行 processRollback() 方法,執行回滾;
- 執行 triggerBeforeCompletion() 方法,觸發前置完成事件,底層調用事務同步 TransactionSynchronization 的 beforeCompletion() 方法;
- 判斷一下條件,執行各自的邏輯
- 如果有保存點,那就執行事務狀態的 rollbackToHeldSavepoint() 方法,回滾到保存點;
- 如果是新事物,執行 doRollback() 方法,進行回滾;
- 都不是,則執行 doSetRollbackOnly() 方法,給事務對象設置回滾標識,讓上層事務進行回滾。
- 執行 triggerAfterCompletion() 方法,執行完成操作,底層調用事務同步 TransactionSynchronization 的 afterCompletion() 方法;
- 最後執行 cleanupAfterCompletion() 方法,進行清除操作。
- 新的同步則執行清理線程本地化操作;
- 新的事務,則執行釋放連接操作;
- 如果有掛起的資源,恢復掛起的資源。
- 不是的話,就執行 processCommit() 方法,進行提交。
- 執行 triggerBeforeCommit() 方法,執行提交前的操作,底層調用事務同步 TransactionSynchronization 的 beforeCommit() 方法;
- 執行 triggerBeforeCompletion() 方法,執行完成前的操作,底層調用事務同步 TransactionSynchronization 的 beforeCompletion() 方法;
- 判斷一下條件,執行各自的邏輯;
- 如果有保存點,則執行事務狀態的 releaseHeldSavepoint() 方法,釋放保存點;
- 如果是新的事務,則執行 doCommit() 方法,進行提交;
- 全局回滾時提前失敗檢查。
- 執行 triggerAfterCompletion() 方法,執行完成之後的操作,底層調用事務同步 TransactionSynchronization 的 afterCompletion() 方法;
- 遇到異常時,執行 doRollbackOnCommitException() 方法,進行回滾;
- 沒有遇到異常,執行 triggerAfterCommit() 方法,執行完成之後的操作;
- 最後執行 cleanupAfterCompletion() 方法,進行清理操作。
- 新的同步則執行清理線程本地化操作;
- 新的事務,則執行釋放連接操作;
- 如果有掛起的資源,恢復掛起的資源。
總結
上面就是 spring tx 模板的核心流程了。回顧下:
- 通過 @EnableTransactionManagement 註解,導入了 TransactionManagementConfigurationSelector 類;
- TransactionManagementConfigurationSelector 類,它導入了AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 類;
- AutoProxyRegistrar 類負責註冊一個 InfrastructureAdvisorAutoProxyCreator 類,它是一個創建代理對象的後置處理器;
- ProxyTransactionManagementConfiguration 類是創建了負責處理事務的增強器 BeanFactoryTransactionAttributeSourceAdvisor、還有處理事務屬性的事務屬性源 AnnotationTransactionAttributeSource、以及對目標方法進行事務處理攔截的事務攔截器 TransactionInterceptor,這三個 bean;
- 其中在調用目標方法時,通過事務攔截器 TransactionInterceptor 對目標方法進行攔截;
- 攔截有創建以及初始化事務相關信息(事務狀態、事務對象、事務同步等信息)、執行目標方法、提交或者回滾事務、清理事務等流程。