【源码剖析】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容器 中,添加了 两个后置处理器:
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
其实,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
等 框架技术进行深度剖析!
觉得有所帮助的同学请 关注 并 点赞,谢谢!