【源码剖析】Spring 事务 详解

在之前的博文中,本人讲解了 Spring IOC 和 Spring AOP 的 核心源码
在本人讲解 Spring的API使用 系列的博文中,本人也说过:
Spring Framework 框架最 核心 的三个功能,就是Spring IOC、Spring AOP和Spring 事务管理
那么,在本文中,本人就来讲解下 Spring 事务管理 的相关源码:
API 调用:
操作表:


配置类:
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");
}
}
运行结果:



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

其实,读了这么久源码,我们也能知道:
Spring系列框架 中
@EnableXxx 注解的 核心,在于 该注解的@Import注解
那么,我们来看看导入的 TransactionManagementConfigurationSelector类,做了什么:
TransactionManagementConfigurationSelector 类:

我们能看到:
导入这个类,就相当于在 Spring容器 中,添加了 两个后置处理器:
AutoProxyRegistrarProxyTransactionManagementConfiguration
其实,Spring 事务 是依靠 Spring AOP 搭建起来的功能
但是,在上面的案例中,本人并 没有 明文标注 开启AOP,这是为什么呢?
我们来看下 AutoProxyRegistrar类 的 源码:
AutoProxyRegistrar 类:

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

我们可以看到:
AutoProxyRegistrar类实现了 开启AOP注解所导入的类 的 部分功能
(没完成的功能,只有 exposeProxy属性,
也就是说:如果我们 仅开启事务,不能使用 AOP 的 当前线程Proxy)
既然在上文中,本人已经说明,Spring 事务 底层是通过 Spring AOP 实现的
那么,本人现在来讲解下,每一个 标注了@Transactional注解 的 方法或类,是怎么被解析成 相应的advisor 的:
advisor转换:
在上文中,本人讲解了 @EnableTransactionManagement注解 的作用,就是导入 TransactionManagementConfigurationSelector后置处理器 和 ProxyTransactionManagementConfiguration类
但是,本人还未讲解 ProxyTransactionManagementConfiguration类
ProxyTransactionManagementConfiguration 类:

我们可以看到:
ProxyTransactionManagementConfiguration类,本质上是一个 config类
并且,它的作用是:向 Spring容器 导入 BeanFactoryTransactionAttributeSourceAdvisor
并且 给beanFactoryTransactionAttributeSourceAdvisor 设置了 transactionInterceptor 和 transactionAttributeSource
那么,现在本人来展示下 BeanFactoryTransactionAttributeSourceAdvisor类:
BeanFactoryTransactionAttributeSourceAdvisor 类:

可以看到:
BeanFactoryTransactionAttributeSourceAdvisor 本质上,就是
Spring AOP的一个advisor
那么,这个advisor是怎么匹配到 @Transactional注解 的呢?
答曰:
通过上面代码中,所设置的 transactionAttributeSource 属性
现在,本人来展示下 AnnotationTransactionAttributeSource类 的内容,来讲解下 是如何进行匹配@Transactional注解的:
AnnotationTransactionAttributeSource 类:

我们能看到:
创建的 AnnotationTransactionAttributeSource类 的对象,其中添加了一个 SpringTransactionAnnotationParser类
而我们在代码中,标注的 @Transactional注解:
至此,被标注的 @Transactional注解 就会被扫描到
那么,扫描到之后,是怎么进行 拦截增强 的呢?
当advisor创建完毕之后,在我们的 Spring AOP 生成代理对象 的过程当中,
就会对 原方法 加以增强,并创建出代理对象,供我们调用:
调用逻辑:
上文中给它设置的 transactionInterceptor,就是拦截 每个匹配上的方法 的 具体拦截逻辑:
事务处理 大体架构:

我们跟进去:

可以看到:
在上面的代码中,获取到了 事务属性 和 切入点信息
继续向下看:

可以看到:
开启事务、提交事务、回滚、清空当前事务信息的逻辑,都在本方法中实现
我们跟进 368行 代码:

继续跟进 572行 代码:
获取 事务对象:

获取 数据库连接对象:
我们跟进 347行 代码:

我们继续跟进 242行 代码 的 obtainDataSource()方法:

可以看到:
数据库连接对象,就在 ConnectionHolder类型的对象 中
我们继续看 获取事务对象 的逻辑:
嵌套事务 的处理:

可以看到:
设置的 事务传播级别 不同,对于 嵌套事务 的处理逻辑就不同
那么,本人再来带同学们看看 挂起事务(368行代码) 的 具体实现流程:

那么,什么时候会将 挂起的事务信息,返回保存在 当前事务信息 中呢?
答曰:
提交事务或者回滚事务的处理逻辑中
至此,Spring 事务 的核心代码,就讲解完毕了!
有了本人之前讲解 Spring AOP 博文阅读经验 的同学,相信很容易就能看懂 本篇博文的讲解流程!
那么,至此,《Spring Framework》系列的 核心源码讲解 就到此结束了!
相信跟了三篇源码流程,同学们对于阅读源码的能力一定有所提高!
在之后的博文中,本人将对 Spring MVC、Mybatis 的核心源码进行剖析
并在 工作稳定后,闲暇之余,更新 Netty、Dubbo、Zookeeper、Spring Cloud Alibaba 等 框架技术进行深度剖析!
觉得有所帮助的同学请 关注 并 点赞,谢谢!




浙公网安备 33010602011771号