【源码剖析】Spring 事务 详解

shadowLogo

在之前的博文中,本人讲解了 Spring IOCSpring AOP核心源码

在本人讲解 Spring的API使用 系列的博文中,本人也说过:

Spring Framework 框架核心 的三个功能,就是 Spring IOCSpring AOPSpring 事务管理

那么,在本文中,本人就来讲解下 Spring 事务管理 的相关源码:

API 调用:

操作表:

account表
inventory


配置类:

package edu.youzg.tx.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@EnableTransactionManagement
@Configuration
@ComponentScan("edu.youzg.tx")
public class YouzgConfig {

    /**
     * 数据库连接 配置
     */
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/explore_source?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

    /**
     * 事务管理器 配置
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * jdbc模板 配置
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

}

业务类:

package edu.youzg.tx.service;

import edu.youzg.tx.pojo.Account;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @Author: Youzg
 * @CreateTime: 2021-03-22 20:23
 * @Description: 带你深究Java的本质!
 */
@Service
public class AccountService {

    @Resource
    private JdbcTemplate jdbcTemplate;

    //public void add(Account account) {
    //    String sql = "insert into account(id, name, money) values (?, ?, ?)";
    //    int add = jdbcTemplate.update(sql, account.getId(), account.getName(), account.getMoney());
    //}
    //
    //public void save(Account account) {
    //    String sql = "update account set money=? where name=?";
    //    int update = jdbcTemplate.update(sql, account.getMoney(), account.getName());
    //}

    /**
     * 查询 货物价格
     *
     * @param goodsId 货物id
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Integer selectInventoryCost(Integer goodsId) {
        String selectSql = "select cost from inventory where id=?";
        Integer cost = jdbcTemplate.queryForObject(selectSql, new Object[] { goodsId }, Integer.class);
        return cost;
    }

    /**
     * 减少 库存
     *
     * @param goodsId 货物id
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void decriseInventoryCount(Integer goodsId) {
        String decriseSql = "update inventory set count=count-1 where id=?";
        jdbcTemplate.update(decriseSql, goodsId);
    }

    /**
     * 扣除 用户金额
     *
     * @param accountName 用户名
     * @param cost 开销
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.SUPPORTS)
    public void deductAccountMoney(String accountName, Integer cost) {
        String decriseSql = "update account set money=account.money-? where name=?";
        jdbcTemplate.update(decriseSql, cost, accountName);
    }

    /**
     * 用户下单
     * @param goodsId 货物id
     * @param accountName 用户名
     */
    @Transactional(rollbackFor = Exception.class)
    public void sold(Integer goodsId, String accountName) {
        decriseInventoryCount(goodsId); // 修改 库存表
        Integer cost = selectInventoryCost(goodsId);
        System.out.println(1/0);    // “故意” 的异常
        deductAccountMoney(accountName, cost);  // 修改 账户表
        System.out.println("购买成功!");
    }

}

测试类:

package edu.youzg.tx.demo;

import edu.youzg.tx.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.sql.SQLException;

/**
 * @Author: Youzg
 * @CreateTime: 2021-03-21 18:33
 * @Description: 带你深究Java的本质!
 */
public class YouzgDemo {

    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new AnnotationConfigApplicationContext("edu.youzg.tx");
        AccountService service = context.getBean("accountService", AccountService.class);
        service.sold(2, "youzg");
    }

}

运行结果:

故意的结果

account

inventory


首先,我们来看看 开启事务注解 —— @EnableTransactionManagement注解 的内容:

@EnableTransactionManagement注解:

@EnableTransactionManage注解
其实,读了这么久源码,我们也能知道:

Spring系列框架@EnableXxx 注解核心,在于 该注解的@Import注解

那么,我们来看看导入的 TransactionManagementConfigurationSelector类,做了什么:

TransactionManagementConfigurationSelector 类:

TransactionManagementConfigurationSelector
我们能看到:

导入这个类,就相当于在 Spring容器 中,添加了 两个后置处理器

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration

其实,Spring 事务 是依靠 Spring AOP 搭建起来的功能
但是,在上面的案例中,本人并 没有 明文标注 开启AOP,这是为什么呢?

我们来看下 AutoProxyRegistrar类源码

AutoProxyRegistrar 类:

AutoProxyRegistrar

我们再来对比下 开启 Spring AOP 的注解,所导入的类:
AOP

我们可以看到:

AutoProxyRegistrar类 实现了 开启AOP注解所导入的类 的 部分功能
(没完成的功能,只有 exposeProxy属性
也就是说:如果我们 仅开启事务,不能使用 AOP 的 当前线程Proxy)


既然在上文中,本人已经说明,Spring 事务 底层是通过 Spring AOP 实现的

那么,本人现在来讲解下,每一个 标注了@Transactional注解方法或类,是怎么被解析成 相应的advisor 的:

advisor转换:

在上文中,本人讲解了 @EnableTransactionManagement注解 的作用,就是导入 TransactionManagementConfigurationSelector后置处理器ProxyTransactionManagementConfiguration类

但是,本人还未讲解 ProxyTransactionManagementConfiguration类

ProxyTransactionManagementConfiguration 类:

ProxyTransactionManagementConfiguration
我们可以看到:

ProxyTransactionManagementConfiguration类,本质上是一个 config类
并且,它的作用是:

向 Spring容器 导入 BeanFactoryTransactionAttributeSourceAdvisor
并且 给beanFactoryTransactionAttributeSourceAdvisor 设置了 transactionInterceptortransactionAttributeSource

那么,现在本人来展示下 BeanFactoryTransactionAttributeSourceAdvisor类

BeanFactoryTransactionAttributeSourceAdvisor 类:

继承关系
可以看到:

BeanFactoryTransactionAttributeSourceAdvisor 本质上,就是 Spring AOP 的一个 advisor


那么,这个advisor是怎么匹配到 @Transactional注解 的呢?
答曰:

通过上面代码中,所设置的 transactionAttributeSource 属性
具体实现

现在,本人来展示下 AnnotationTransactionAttributeSource类 的内容,来讲解下 是如何进行匹配@Transactional注解的:

AnnotationTransactionAttributeSource 类:

AnnotationTransactionAttributeSource
我们能看到:

创建的 AnnotationTransactionAttributeSource类 的对象,其中添加了一个 SpringTransactionAnnotationParser类
而我们在代码中,标注的 @Transactional注解
解析

至此,被标注的 @Transactional注解 就会被扫描到

那么,扫描到之后,是怎么进行 拦截增强 的呢?


当advisor创建完毕之后,在我们的 Spring AOP 生成代理对象 的过程当中,
就会对 原方法 加以增强,并创建出代理对象,供我们调用:

调用逻辑:

上文中给它设置的 transactionInterceptor,就是拦截 每个匹配上的方法具体拦截逻辑

事务处理 大体架构:

拦截逻辑
我们跟进去:
属性获取
可以看到:

在上面的代码中,获取到了 事务属性切入点信息

继续向下看:
事务处理逻辑
可以看到:

开启事务提交事务回滚清空当前事务信息 的逻辑,都在本方法中实现

我们跟进 368行 代码:
当前事务信息

继续跟进 572行 代码:

获取 事务对象:

获取事务对象

获取 数据库连接对象:

我们跟进 347行 代码:
连接对象
我们继续跟进 242行 代码 的 obtainDataSource()方法
返回值
可以看到:

数据库连接对象,就在 ConnectionHolder类型的对象


我们继续看 获取事务对象 的逻辑:

嵌套事务 的处理:

事务传播特性
可以看到:

设置的 事务传播级别 不同,对于 嵌套事务 的处理逻辑就不同

那么,本人再来带同学们看看 挂起事务(368行代码)具体实现流程
挂起事务

那么,什么时候会将 挂起的事务信息,返回保存在 当前事务信息 中呢?
答曰:

提交事务 或者 回滚事务 的处理逻辑中


至此,Spring 事务 的核心代码,就讲解完毕了!

有了本人之前讲解 Spring AOP 博文阅读经验 的同学,相信很容易就能看懂 本篇博文的讲解流程!

那么,至此,《Spring Framework》系列的 核心源码讲解 就到此结束了!

相信跟了三篇源码流程,同学们对于阅读源码的能力一定有所提高!

在之后的博文中,本人将对 Spring MVCMybatis 的核心源码进行剖析
并在 工作稳定后,闲暇之余,更新 NettyDubboZookeeperSpring Cloud Alibaba 等 框架技术进行深度剖析!

觉得有所帮助的同学请 关注点赞,谢谢!
学计算机

posted @ 2021-03-23 19:43  在下右转,有何贵干  阅读(102)  评论(0编辑  收藏  举报