死磕 Spring 之 AOP 篇,带你全解 Spring 事务!

本文详细介绍了Spring事务管理,包括事务的物理和逻辑概念,传播级别,以及如何在Spring MVC和Spring Boot中使用。重点讲解了Spring事务的核心API,如@Transaction、PlatformTransactionManager、TransactionInterceptor等,并通过源码分析了事务的创建、提交和回滚过程。此外,还探讨了事务切面和@EnableTransactionManagement注解的工作原理。
摘要由CSDN通过智能技术生成

前序

我们先了解一下 Spring 事务里面的“物理事务”和“逻辑事务”,所谓的“物理事务”是指 JDBC 的事务,上一次事务和本次事务之间是没有其他事务的,在执行一条命令(默认行为自动提交)都会产生一个事务,如果把 autocommit 设置为 false,需要主动 commit 才完成一个事务。所谓的“逻辑事务”是 Spring 对 JDBC 的一个抽象,例如 Spring 默认的事务传播行为是 REQUIRED,当执行 @Transactional 注解标注的方法时,如果此时正处于一个事务(物理事务)中,那么加入到这个事务中,你可以理解为创建了一个“逻辑事务”,进行提交的时候不会执行 Connection 的 commit 方法,而是在外面的“物理事务”中进行 commit 时一并完成本次事务。

此外,撸了35天,整理最强“全家桶”脑图+面试+进阶学习:Spring+Cloud+Security+Boot+MVC+Spring5,且全篇分为三个部分:脑图篇+面试篇+进阶学习篇,资源皆可分享 ☛ ☛ 请看此处

01 Spring 事务的传播级别

  • REQUIRED:默认传播级别,如果正处于一个事务中,则加入;否则,创建一个事务

  • SUPPORTS:如果正处于一个事务中,则加入;否则,不使用事务

  • MANDATORY:如果当前正处于一个事务中,则加入;否则,抛出异常

  • REQUIRES_NEW:无论如何都会创建一个新的事务,如果正处于一个事务中,会先挂起,然后创建

  • NOT_SUPPORTED:不使用事务,如果正处于一个事务中,则挂起,不使用事务

  • NEVER:不使用事务,如果正处于一个事务中,则抛出异常

  • NESTED:嵌套事务,如果正处于一个事务中,则创建一个事务嵌套在其中(MySQL 采用 SAVEPOINT 保护点实现的);否则,创建一个事务

关于 Spring 事务传播级别更多的细节在接下来的源码中进行讨论

02 Spring 事务的使用示例

相信看到这篇文章的你对于 @Transactional 注解的使用肯定非常了解,不过这里还是列举以下它的使用方式

2.1 Spring MVC

引入 Spring 事务相关依赖后,在 Spring MVC 中有两种(XML 配置和注解)驱动 Spring 事务的方式,如下面所示:

方式一:


<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 定义一个数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">...</bean>
    <!-- 事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 指定数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 事务模块驱动,指定使用上面定义事务管理器,默认值为 transactionManager -->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

方式二:

需要在 Spring 能扫描的一个 Bean 上添加一个 @EnableTransactionManagement 注解,然后添加一个 TransactionManagementConfigurer 实现类,如下:


package tk.mybatis.simple.controller;

import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig implements TransactionManagementConfigurer {

    @Resource
    DataSource dataSource;

    @Override
    public TransactionManager annotationDrivenTransactionManager() {
        // 返回一个事务管理器,设置数据源
        return new DataSourceTransactionManager(dataSource);
    }
}

此时你可以使用 @Transactional 注解标注在方法(或者类)上面,使得方法的执行处于一个事务中,如下:

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public void update(){
    try {
        // 数据库操作
    } catch (Exeception e){
        // 将事务状态设置为回滚
        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
    }
}

2.2 Spring Boot

在 Spring Boot 中我们使用 @Transactional 注解的时候好像不需要 @EnableTransactionManagement 注解驱动 Spring 事务模块,这是为什么?和 Spring AOP 的 @EnableAspectJAutoProxy 注解类似,会有一个 TransactionAutoConfiguration 事务自动配置类,我们一起来看看:


@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public TransactionManagerCustomizers platformTransactionManagerCustomizers(
            ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) {
        return new TransactionManagerCustomizers(customizers.getIfAvailable());
    }

    @Configuration
    @ConditionalOnSingleCandidate(PlatformTransactionManager.class)
    public static class TransactionTemplateConfiguration {

        private final PlatformTransactionManager transactionManager;

        public TransactionTemplateConfiguration(
                PlatformTransactionManager transactionManager) {
            this.transactionManager = transactionManager;
        }

        @Bean
        @ConditionalOnMissingBean
        public TransactionTemplate transactionTemplate() {
            return new TransactionTemplate(this.transactionManager);
        }
    }

    @Configuration
    @ConditionalOnBean(PlatformTransactionManager.class)
    @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
    public static class EnableTransactionManagementConfiguration {
        @Configuration
        @EnableTransactionManagement(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
        public static class JdkDynamicAutoProxyConfiguration { }

        @Configuration
        @EnableTransactionManagement(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
        public static class CglibAutoProxyConfiguration { }
    }
}

是不是很熟悉,只要存在 PlatformTransactionManager 这个 Class 对象就会将这个 Bean 注册到 IoC 容器中,里面涉及到一些 @Conditional 注解,这里就不一一解释了。可以看到其中会有 @EnableTransactionManagement 注解,是不是和在 Spring MVC 中以注解驱动 Spring 事务的方式一样,但是好像没有 PlatformTransactionManager 事务管理器。别急,我们看到这个自动配置类上面会有 @AutoConfigureAfter({DataSourceTransactionManagerAutoConfiguration.class}) 注解,表示会先加载 DataSourceTransactionManagerAutoConfiguration 这个自动配置类,我们一起来看看:


@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

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

        private final DataSource dataSource;

        private final TransactionManagerCustomizers transactionManagerCustomizers;

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

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

可以看到会注入一个 DataSourceTransactionManager 事务管理器,关联这个当前 DataSource 数据源对象

好了,通过上面的使用示例我们可以注意到 @EnableTransactionManagement 注解可以驱动整个 Spring 事务模块,当然,<annotation-driven /> 标签的原理和注解差不多,前面也讲述了非常多 Spring 自定义标签的实现原理,这里我们就不分析了,那么我们一起来看看 @EnableTransactionManagement 这个注解

03 核心 API

在开始查看 Spring 事务的源码之前,我想有必要先简单介绍一下涉及到的一些主要的 API,对 Spring 事务的源码有一个印象,如下:

  • Spring 事务 @Enable 模块驱动 - @EnableTransactionManagement

  • Spring 事务注解 - @Transactional

  • Spring 事务事件监听器 - @TransactionalEventListener

  • Spring 事务定义 - TransactionDefinition

  • Spring 事务状态 - TransactionStatus

  • Spring 平台事务管理器 - PlatformTransactionManager

  • Spring 事务代理配置 - ProxyTransactionManagementConfiguration

  • Spring 事务 PointAdvisor 实现 - BeanFactoryTransactionAttributeSourceAdvisor

  • Spring 事务 MethodInterceptor 实现 - TransactionInterceptor

  • Spring 事务属性源 - TransactionAttributeSource

简单介绍 Spring 事务:

  1. 需要通过 @EnableTransactionManagement 注解驱动整个 Spring 事务模块

  2. 可以通过 @Transactional 注解定义在某个类或者方法上面定义一个事务(传播性、隔离性等),开启事务

  3. ProxyTransactionManagementConfiguration 代理配置类用来定义一个 BeanFactoryTransactionAttributeSourceAdvisor 切面,是一个用于 Spring 事务的 AOP 切面

  4. Spring 事务底层就是通过 Spring AOP 实现的,可以在上面看到有一个 PointcutAdvisor 切面,关联的 Pointcut 内部有一个 TransactionAttributeSource 对象,会借助于 TransactionAnnotationParser 解析器解析 @Transactional 注解,将这个事务定义的一些属性封装成一个 TransactionDefinition 事务定义对象

  5. Spring AOP 拦截处理在 TransactionInterceptor 事务拦截器中,先借助 PlatformTransactionManager 平台事务管理器创建 TransactionStatus 事务对象,里面包含了 Transaction 事务,将 autocommit 自动提交关闭,方法的执行也就处于一个事务中

  6. 事务的相关属性会保存在许多 ThreadLocal 中,例如 DataSource、Connection 和 SqlSession 等属性,交由一个 TransactionSynchronizationManager 事务同步管理器进行管理,所以说 Spring 事务仅支持在一个线程中完成

Spring 事务非常复杂,接下来我们逐步分析

04 @EnableTransactionManagement 注解驱动


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

    /**
     * 默认优先使用 JDK 动态代理
     */
    boolean proxyTargetClass() default false;

    /**
     * 默认使用 Spring AOP 代理模式
     */
    AdviceMode mode() default AdviceMode.PROXY;

    int order() default Ordered.LOWEST_PRECEDENCE;
}

可以看到有一个 @Import 注解,它的值是一个 TransactionManagementConfigurationSelector 类,也就是说 Spring 事务的驱动入口在这里面

TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            // 默认使用代理模式
            case PROXY:
                return new String[]{
                        // 注册一个 InfrastructureAdvisorAutoProxyCreator 对象,目的是创建代理对象
                        AutoProxyRegistrar.class.getName(),
                        // 【关键】注册一个 Spring 事务代理配置类
                        ProxyTransactionManagementConfiguration.class.getName()};
            // 选择 AspectJ 模式
            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);
    }
}

可以看到默认情况下会注册两个 Bean

  • AutoProxyRegistrar,注册一个 InfrastructureAdvisorAutoProxyCreator 对象,目的是创建代理对象,在讲解 Spring AOP 的时候讲述过,这里不再赘述

  • ProxyTransactionManagementConfiguration,一个 Spring 务代理配置类

05 ProxyTransactionManagementConfiguration

org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration,Spring 事务代理配置类,定义好一个 AOP 切面


@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        // <1> 创建 PointcutAdvisor 对象,作为 @Transactional 注解的一个切面
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        // <2> 【Pointcut】设置 AnnotationTransactionAttributeSource,被关联在 Pointcut 中
        // 借助于 TransactionAnnotationParser 解析器解析 @Transactional 注解
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        // <3> 【Advice】设置 Advice 为 TransactionInterceptor 事务拦截器
        advisor.setAdvice(transactionInterceptor());
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

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

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        // 创建 TransactionInterceptor 事务拦截器(MethodInterceptor 对象)
        TransactionInterceptor interceptor = new TransactionInterceptor();
        // 设置这个 AnnotationTransactionAttributeSource 对象,@Bean 注解标注的方法返回的都是同一个对象
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            // 设置默认的事务管理器
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }
}

可以看到会注册三个 Bean:

  1. BeanFactoryTransactionAttributeSourceAdvisor 切面,这个 PointcutAdvisor 对象关联的 Pointcut 切点用于筛选 @Transactional 注解的方法(标注在类上也可以),在关联的 Advice 中会进行事务的拦截处理

  2. Advice 通知,就是一个 TransactionInterceptor 方法拦截器,关联着一个 AnnotationTransactionAttributeSource 对象

  3. AnnotationTransactionAttributeSource 事务属性资源对象,被 Pointcut 和 Advice 关联,用于解析 @Transactional 注解,在它的构造方法中会添加一个 SpringTransactionAnnotationParser 事务注解解析器,用于解析 @Transactional 注解,如下:


// AnnotationTransactionAttributeSource.java
public AnnotationTransactionAttributeSource() {
    this(true);
}

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

06 PointcutAdvisor 事务切面

org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor,Spring 事务切面,如下:


public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    /**
     * 事务属性源对象,用于解析 @Transactional 注
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值