侧边栏壁纸
博主头像
Leokoの小破站博主等级

行动起来,活在当下

  • 累计撰写 18 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Spring 事务源码

Leoko
2024-09-29 / 0 评论 / 0 点赞 / 150 阅读 / 41103 字

想了解事务源码的小伙伴肯定熟悉 Spring 中事务的使用,先来回顾一下。

  1. 导入 spring-tx 包;

  2. 配置类加上 @EnableTransactionManagement 注解;

  3. 需要事务控制的方法上加上 @Transactional 注解。

一个最基本的事务控制就完成了。

对于 SpringBoot 项目,不需要加上 @EnableTransactionManagement 注解,因为有 SpringBoot 自动装配。在 spring-boot-autoconfigure 包下 spring.factories 文件中,EnableAutoConfiguration 有一个 SPI 实现 TransactionAutoConfiguration,里面包含了 @EnableTransactionManagement 注解。

所以,原理是一样的,只是配置方式不同。

1. @EnableTransactionManagement 注解

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

还是老套路,@EnableXXX + @Import 注解组合,来看一下 TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    public TransactionManagementConfigurationSelector() {
    }
​
    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{this.determineTransactionAspectClass()};
        default:
            return null;
        }
    }
}

@EnableTransactionManagement 注解默认使用 PROXY 模式来增强事务,所以 selectImports 方法返回两个类的全限定类名:AutoProxyRegistrarProxyTransactionManagementConfiguration,事务最终起作用的是上述两个类的功能。

2. AutoProxyRegistrar

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ......
        Object mode = candidate.get("mode");
        Object proxyTargetClass = candidate.get("proxyTargetClass");
        if (mode == AdviceMode.PROXY) {
            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            if ((Boolean) proxyTargetClass) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                return;
            }
        }
        ......
    }
}

实现了 ImportBeanDefinitionRegistrar 接口,手动向 IOC 容器中导入组件。如果 @EnableTransactionManagement 注解中设置 adviceModePROXY (默认PROXY),则会利用 AopUtils 创建组件,并且如果 @EnableTransactionManagement 设置 proxyTargetClass 为true,则还会额外导入组件(默认为false)。

2.1 AopConfigUtils.registerAutoProxyCreatorIfNecessary

// AopConfigUtils
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAutoProxyCreatorIfNecessary(registry, null);
}
​
@Nullable
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");
​
    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;
}

可以看到最终是往 BeanDefinitionRegistry 注册了一个类为 InfrastructureAdvisorAutoProxyCreatorbeanDefinition,名字为静态变量 AUTO_PROXY_CREATOR_BEAN_NAME 的值 :org.springframework.aop.config.internalAutoProxyCreator。看到这个名字是不是很熟悉?AOP 那块有讲过,不记得的或者没看过的小伙伴可以去看一下哦。

AOP 中名字为 AUTO_PROXY_CREATOR_BEAN_NAMEbeanDefinition 对应的类是 AnnotationAwareAspectJAutoProxyCreator,那和事务的这个不是冲突了吗?别急,仔细看下,注册到 BeanDefinitionRegistry 之前还有个判断 !cls.getName().equals(apcDefinition.getBeanClassName()),判断要注册的 beanDefinition 所属类名和已存在的是否一致,即 AnnotationAwareAspectJAutoProxyCreatorInfrastructureAdvisorAutoProxyCreator 的判断,而这个是根据优先级判断的,那个优先级高就用哪个。而优先级的判断是根据数组里面的下标。

// AopConfigUtils
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) {
    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");
}

AnnotationAwareAspectJAutoProxyCreator 在数组最后,所以优先级比 InfrastructureAdvisorAutoProxyCreator 高。

因此,在同时引入了 Spring AOPSpring TX 的情况下,最终生效的是AnnotationAwareAspectJAutoProxyCreator,即使 AnnotationAwareAspectJAutoProxyCreator 先被注册 ,最终都会被覆盖 。点开源码比较,AnnotationAwareAspectJAutoProxyCreator 多了 AspectJ 相关的逻辑。

2.2 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry)

设置事务增强的类全部使用 Cglib 代理类的方式,对应 AOPAbstractAutoProxyCreator # createProxy 方法中的 proxyFactory.isProxyTargetClass()

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}

3. ProxyTransactionManagementConfiguration

这个配置类有三个组件。

3.1 TransactionAttributeSource

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

仅仅创建了一个 AnnotationTransactionAttributeSource

3.1.1 TransactionAttributeSource

先看一下接口。

public interface TransactionAttributeSource {
    default boolean isCandidateClass(Class<?> targetClass) {
        return true;
    }
​
    @Nullable
    TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}

主要是下面的 getTransactionAttribute 方法,方法参数是一个 方法 + 类。实际上就是根据一个类 + 方法,解析转换为 TransactionAttribute。而 TransactionAttribute 继承自 TransactionDefinition,所以可以这样理解:TransactionAttribute 可以根据一个具体的类中的方法,解析转换为一个 TransactionDefinitionTransactionAttribute )。

3.1.2 AnnotationTransactionAttributeSource

直接定位类注释中关键的一句:

This class reads Spring's JDK 1.5+ @Transactional annotation and exposes corresponding transaction attributes to Spring's transaction infrastructure.

由此可见 AnnotationTransactionAttributeSource 是读取和解析标注有 @Transactional 注解的方法的。怎么解析的放到下面讲。

3.2 TransactionInterceptor 事务拦截器

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

TransactionInterceptor 实现了 MethodInterceptorAOP 部分讲过MethodInterceptor 接口是 AOP 增强的核心拦截器接口,利用 AOP 生成的代理对象中都会包含一组 MethodInterceptor 接口的实现类对象。并且它这里还依赖了上面说到的 TransactionAttributeSource

3.3 BeanFactoryTransactionAttributeSourceAdvisor 事务增强器

@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);
    // 取@EnableTransactionManagement的order属性
    if (this.enableTx != null) {
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
}

使用 BeanFactoryTransactionAttributeSourceAdvisor 作为事务增强器的实现,并给它配置了 TransactionAttributeSource。作为一个增强器,肯定得有 AdvicePointCut,但是看代码只设置了 Advice(即 transactionInterceptor),PointCut呢?我们进入 BeanFactoryTransactionAttributeSourceAdvisor 这个类里面:

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

	@Nullable
	private TransactionAttributeSource transactionAttributeSource;

	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};
    ......
}

哦,原来切入点在类内部就已经初始化好了,类型是 TransactionAttributeSourcePointcut,而且实现了 getTransactionAttributeSource 方法,返回的正好是配置类中的 transactionAttributeSource,这个方法有什么用呢?直接看 TransactionAttributeSourcePointcut 的核心 matches 方法:

// TransactionAttributeSourcePointcut
public boolean matches(Method method, Class<?> targetClass) {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

就是拿 TransactionAttributeSource 去根据方法和方法所属类,判断是否有对应的事务定义信息(是否被 @Transactional 注解标注)。

4. 代理类生成

事务代理类的生成和 AOP 代理类的生成是一样的,所以直接定位到核心处理点,前面的逻辑就不再赘述了。回到老地方:

// AbstractAdvisorAutoProxyCreator
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

advisorRetrievalHelper.findAdvisorBeans()AOP 那块讲过,就是找出实现了 Advisor 接口的 bean,而前面说到的 BeanFactoryTransactionAttributeSourceAdvisor 正好匹配。可以随便写个测试类,方法加上 @Transactional 注解,并注入到 IOC,将断点打到这,查看结果。

image-20231026194656624

Advisor 找到之后,就是判断是否能匹配了。这块的判断和 AOP 有点区别。

image-20231026195427237

匹配调用的是 methodMatcher.matches,而 AOP 中匹配是上面那一个(251行)。

methodMatcher.matches 方法就是前面说过的 TransactionAttributeSourcePointcut 中的 matches 方法,只不过先前只是简单看了一下,下面来深入解析一下:

// TransactionAttributeSourcePointcut
public boolean matches(Method method, Class<?> targetClass) {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

// AbstractFallbackTransactionAttributeSource(AnnotationTransactionAttributeSource 的父类)
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    .....
    // First, see if we have a cached value.
    Object cacheKey = getCacheKey(method, targetClass);
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
    if (cached != null) {
       .......
    }
    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;
    }
}

先查缓存,缓存中没有才调用 computeTransactionAttribute 方法生成 TransactionAttribute

// AbstractFallbackTransactionAttributeSource
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // 非 public 方法返回 null
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }
    .........
}

// AnnotationTransactionAttributeSource
protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
}

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        if (attr != null) {
            return attr;
        }
    }
    return null;
}

最终使用 TransactionAnnotationParser 解析方法并转换,而 TransactionAnnotationParser 有三个实现类,那肯定不用说就是 SpringTransactionAnnotationParser 了。

image-20231027105853255

// SpringTransactionAnnotationParser
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
        element, Transactional.class, false, false);
    if (attributes != null) {
        return parseTransactionAnnotation(attributes);
    }
    else {
        return null;
    }
}

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());
        ......
}

就是找对应的方法上面 @Transactional 注解相关属性如 propagation 传播行为,·封装到 AnnotationAttributes 里面,最终根据 AnnotationAttributes 获取 TransactionAttribute 并返回,注意类型是 RuleBasedTransactionAttribute,继承自 DefaultTransactionAttribute

TransactionAttribute 不为空,那最初的 matches 方法就返回 true,说明增强器 BeanFactoryTransactionAttributeSourceAdvisor 可以匹配该方法(即方法上面有 @Transactional 注解)。

增强器拿到了,接下来就是 createProxy 了,这里不再重复解析了。

5. 代理类执行

由于 AOP 部分已经讲过代理类的执行逻辑,所以这里我们直接定位到 TransactionInterceptorinvoke 方法。

// TransactionInterceptor
public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
        @Override
        @Nullable
        public Object proceedWithInvocation() throws Throwable {
            return invocation.proceed();
        }
        @Override
        public Object getTarget() {
            return invocation.getThis();
        }
        @Override
        public Object[] getArguments() {
            return invocation.getArguments();
        }
    });
}

进入 invokeWithinTransaction 方法,在父类 TransactionAspectSupport 中。这个方法篇幅较长,所以下面拆开来讲。

5.1 获取方法对应的事务定义

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    .......

前面两行代码在 代理类生成章节已经分析过了,这里可以直接从缓存中获取 TransactionAttribute

5.2 决定事务管理器

protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    // Do not attempt to lookup tx manager if no tx attributes are set
    if (txAttr == null || this.beanFactory == null) {
        return getTransactionManager();
    }

    String qualifier = txAttr.getQualifier();
    if (StringUtils.hasText(qualifier)) {
        return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    }
    else if (StringUtils.hasText(this.transactionManagerBeanName)) {
        return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    }
    else {
        TransactionManager defaultTransactionManager = getTransactionManager();
        if (defaultTransactionManager == null) {
            defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
            if (defaultTransactionManager == null) {
                defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
                this.transactionManagerCache.putIfAbsent(
                    DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
            }
        }
        return defaultTransactionManager;
    }
}

代码里获取事务管理的方式有很多种,但实际 Debug 的时候走的是最后一个方式,即通过 beanFactory 获取。

对于 Spring 项目,需要在配置类中手动注入到 IOC 容器。

@Configuration
public class TransactionConfiguration {
    
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("...");
        dataSource.setUsername("...");
        dataSource.setPassword("....");
        return dataSource;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }
    
    @Bean
    public TransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

对于 SpringBoot 项目,引入了相关持久层框架(MyBatis 等),则会自动注入。

5.3 响应式事务的处理

// ......
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);
}
// ......

这里不研究响应式事务,响应式事务用的也不多,感兴趣的可以自行研究。

5.4 事务控制

PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
    // 开启事务
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

    Object retVal;
    try {
        // 执行原方法
        retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
        // 回滚事务	
        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;
}

仔细一看,其实就是类似 AOP 的环绕通知。

5.4.1 创建事务

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

先将原来的 TransactionAttribute 包装为 DelegatingTransactionAttribute。而 tm.getTransaction(txAttr) 这行则是真正开启事务。和原生 JDBC 开启事务一样,tm.getTransaction(txAttr) 最终会在 DataSourceTransactionManagerdoBegin 方法设置自动提交为 false

protected void doBegin(Object transaction, TransactionDefinition definition) {
    .......
    if (con.getAutoCommit()) {
        txObject.setMustRestoreAutoCommit(true);
        if (logger.isDebugEnabled()) {
            logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
        }
        con.setAutoCommit(false);
    }
    .......
}

将断点打在创建事务的下一行,查看返回的 TransactionInfo 包含哪些内容。

image-20231103092946429

可以看到,事务定义信息和状态都封装好了,状态中 completed 值为 false

5.4.2 事务提交

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());
    }
}

txInfo.getTransactionManager().commit 提交事务。commit 方法的实现则是在 AbstractPlatformTransactionManager 这个类中。

// AbstractPlatformTransactionManager
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);
}

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        // ......

            if (status.hasSavepoint()) {
                // 如果当前事务存在保存点,则处理保存点的逻辑
            }
            // 对于新事务,直接提交事务即可
            else if (status.isNewTransaction()) {
                // logger ......
                unexpectedRollback = status.isGlobalRollbackOnly();
                doCommit(status);
            }
    // ......
}

核心是 doCommit 方法,在 DataSourceTransactionManager 中。

// DataSourceTransactionManager
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
        logger.debug("Committing JDBC transaction on Connection [" + con + "]");
    }
    try {
        con.commit();
    }
    catch (SQLException ex) {
        throw translateException("JDBC commit", ex);
    }
}

这里就是熟悉的原生 jdbc 操作了,获取 Connection,然后执行 commit 方法。

5.4.3 事务回滚

@Transactional
public void test() {
    throw new RuntimeException("模拟异常");
}

手动模拟异常,异常会被捕获,进入到下面的 completeTransactionAfterThrowing 方法。

注意这个方法中的 Throwable 参数,这个就业务方法执行过程中抛出的异常。

// TransactionAspectSupport
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        // logger .....
        // 如果当前异常在回滚范围内,则回滚事务
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            } // catch ..... 
        }
        else {
            // 否则依然提交事务
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }// catch ..... 
        }
    }
}

那回滚范围怎么理解呢?我们看 transactionAttribute # rollbackOn 方法,前面分析过 transactionAttribute 被包装成 DelegatingTransactionAttribute

image-20231103162539411

// DelegatingTransactionAttribute
private final TransactionAttribute targetAttribute;

public DelegatingTransactionAttribute(TransactionAttribute targetAttribute) {
    super(targetAttribute);
    this.targetAttribute = targetAttribute;
}

public boolean rollbackOn(Throwable ex) {
    return this.targetAttribute.rollbackOn(ex);
}

DelegatingTransactionAttributetargetAttribute 对应的类型就是 txAttr 原本的类型 RuleBasedTransactionAttribute,我们直接到这个类看对应的方法。

// RuleBasedTransactionAttribute
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;

    if (this.rollbackRules != null) {
        for (RollbackRuleAttribute rule : this.rollbackRules) {
            int depth = rule.getDepth(ex);
            if (depth >= 0 && depth < deepest) {
                deepest = depth;
                winner = rule;
            }
        }
    }

    // User superclass behavior (rollback on unchecked) if no rule matches.
    if (winner == null) {
        // 调用父类 DefaultTransactionAttribute 的方法
        return super.rollbackOn(ex);
    }

    return !(winner instanceof NoRollbackRuleAttribute);
}

中间有一个回滚规则的判断,对于例子中的使用,rollbackRulesnull,所以 winner 也为 ·null,最终是调用父类 DefaultTransactionAttributerollbackOn 方法,看一下父类的回滚规则判断:

// DefaultTransactionAttribute
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

哦,原来是判断业务抛出的异常类型啊。只有异常是 RuntimeExceptionError 这两种类型的异常才能正常执行回滚逻辑,其他异常则依旧正常提交。

对于异常这里补充一下:

Java 中的异常主要分两大类:

  • Exception:程序本身可以处理的异常,可以通过 catch 捕获。Exception 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不 处理)。常见的受检查异常有: IO 相关的异常、ClassNotFoundException。不受检查异常有:空指针异常、数组越界异常等。RuntimeException 及其子类都是不受检查异常,其他则都是受检查异常。

  • Error:程序本身无法处理的错误。如内存溢出(OutOfMemoryError)等。

所以,通俗来讲:业务逻辑出现的空指针等不受检查异常或 Error,会被回滚。而文件读写等,Spring 无法回滚,即使出现异常,也会正常提交。

那如果想对于所有的 Exception,都能执行回滚,要怎么做呢? @Transactional 有一个 rollbackFor 的属性,可以指定回滚异常的类型。所以只需这样写 :@Transactional(rollbackFor = Exception.class)。前面说过 RuleBasedTransactionAttribute 有回滚规则的判断,如果当前事务定义信息没有回滚规则的话就走默认的回滚判断,所以加了之后一定有回滚规则的,不然就回滚不了了,将断点打在回滚规则是否为 null 的地方查看结果。

image-20231106191854145

果然如此,有一个回滚规则,且回滚对应的异常为 Exception。且 rollbackRules 是一个 List 类型,说明可以有多个回滚规则,而 @TransactionalrollbackFor 属性的类型也是 Class 数组,可以指定多种异常类型,正好对应。

if 里面的逻辑就是判断业务抛出的异常类型是否为指定异常RuntimeExceptionException 的子类,depth 就是对应异常树中的位置。如:指定 Exception 为顶级异常,depth 为0;RuntimeExceptionException 的下一级,深度加1,对应的 depth 就是 1;RuntimeException 的直接子类对应的 depth 就是2。以此类推。

简单来说:就是判断业务抛出的异常类型是否为指定异常,是则返回 true,匹配成功;否则依旧执行默认的回滚判断。

rollbackRules 是什么时候初始化的?回到构造 RuleBasedTransactionAttribute 的地方。

// SpringTransactionAnnotationParser
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    ........
    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;
}

取出了 @Transactional 中与回滚相关的属性值,封装为 RollbackRuleAttribute,供后续回滚异常的判断。

判断完成后,就是执行回滚逻辑了,最终还是是原生 jdbc 的事务操作,拿到 Connection ,执行 rollback 方法回滚事务。这里就不再展开讲了。


0

评论区