【Spring事务】深入浅出Spring事务从原理到源码

embedded/2024/12/23 8:46:29/
  1. 什么是事务
    保证业务操作完整性的一种数据库机制 (driver 驱动)
  2. 事务特定 ACID
    A 原子性 (多次操作 要不一起成功 要不一起失败 (部分失败 savepoint))
    C 一致性 (事务开始时数据状态,事务结束是数据状态 一致 )
    I 隔离性 (多个事务不能互相影响,做到隔离)
    D 持久性 (事务操作的结果 ,永久持久化在数据库)
  3. 事务
    单机版本 和 分布式版本事务
  4. spring控制事务
    核心要点:通过AOP方式创建事务。
    方式:编程式事务、声明式事务

一个事务的demo

@Configuration
@ComponentScan("com.qxlx.tx.annotation")
@EnableTransactionManagement
public class AppConfig {@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource) {JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("rootroot");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test");return dataSource;}@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}}
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Override@Transactionalpublic void register(User user) {userDao.save(user);}}
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void save(User user) {jdbcTemplate.update("insert into user(name,age) values (?,?)",user.getName(),user.getAge());}
}

测试类

        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) ioc.getBean("userServiceImpl");User user = new User();user.setName("Qxlx");user.setAge(27);userService.register(user);

我们来思考,有哪些核心流程,其实对应AOP来说,1 前置处理类以及生成代理类的过程 2.在生成代理类之后,通过代理类对象执行最终方法的调用的拦截处理。
其实事务也是基于AOP进行完成功能的,所以整体也是这两个部分。

事务基础

事务属性的目的,为了更好的描述 事务的特点

隔离级别

在这里插入图片描述## 传播属性
在这里插入图片描述## 只读、超时、异常
在这里插入图片描述

前置类的初始化

在AppConfig类 引入了一个注解 @EnableTransactionManagement,我们知道一半Enable注解都是通过Import的方式引入外部类 然后去完成对应的功能。

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {boolean proxyTargetClass() default false;AdviceMode mode() default AdviceMode.PROXY;int order() default Ordered.LOWEST_PRECEDENCE;	
}

所以实际上是引入了两个类。

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

AutoProxyRegistrar

在这里插入图片描述
在这里插入图片描述
查看类结果图,发现其实本质就是BPP的实现类,
在这里插入图片描述
在这里插入图片描述
所以总结一下通过@enable注解 注册一个BPP,然后BPP实例化之后,在实例化自定义bean的时候去生成代理类。剩下的代码就不过看了。

ProxyTransactionManagementConfiguration

在这里插入图片描述
这个TransactionInterceptor 拦截器很关键,在运行具体方法的时候,使用的。

运行时的核心流程

 userService.register(user);

在这里插入图片描述

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);

在这里插入图片描述
在这里插入图片描述

开启事务-createTransactionIfNecessary

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

getTransaction

获取事务的状态,同时开启事务

	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {// new一个 DataSourceTransactionObject对象// 同时从threadLocal中获取Connect datasource为key // 第一次为空Object transaction = doGetTransaction();// 是否存在事务if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 开启事务		doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}
	protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = obtainDataSource().getConnection();txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);con.setAutoCommit(false);}prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the connection holder to the thread.// 绑定连接到ThreadLocal中 if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}

在这里插入图片描述

prepareTransactionInfo

进行TransactionInfo封装,
1.封装TransactionInfo对象, 将新的transactionInfo存储起来到ThreadLocal。
在这里插入图片描述

清除事务信息 cleanupTransactionInfo

将外部事务进行恢复
在这里插入图片描述

事务提交 commitTransactionAfterReturning

	txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

事务回滚 completeTransactionAfterThrowing

原始方法 抛出异常一定回滚吗,
Exception及其子类 默认提交
RuntimeException及其子类 或者Error错误

	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());				}else {// We don't roll back on this exception.// Will still roll back if TransactionStatus.isRollbackOnly() is true.txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}}

可以发现默认抛出的异常时 RuntimeException 子类才会进行事务的回滚,或者是Error的类型。

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

那么到这里就结束了吗,其实上面罗列的只是一个简单的流程。我们知道Spring有几大特性,其中最为复杂的传播属性是Spring特有的机制,那么他是如何完整多个事务嵌套的场景执行的呢?

传播机制原理核心原理

其实大概的流程 如果用伪代码来描述的话大概如下,假设我们执行的流程是这样。

addUser() ; 传播属性为REQUIRED 
addBlack() ; 默认传播属性 REQUIRED_NEW 
1.先执行外部addUser(); 创建连接->反射执行目标方法addUser()->接着执行addBlack()-> (1.挂起addUser事务 2.创建新事务 3.执行addBlack()-> 提交事务 4.恢复addUser事务)-> addBlack 提交事务 完成

其实问题的核心点,我们需要找到挂起事务的流程 以及恢复事务的流程 看看代码是如何实现的,基本上了解spring底层是如何完成这个处理的。

在这里插入图片描述

总结

spring事务整体的核心流程 其实就是在AOP是在IOC基础上,动态创建代理对象,事务在IOC+AOP的基础上 创建事务代理对象,完成一系列的功能。源码内容本身是比较复杂和关系比较错乱,如果想每行源码都搞清楚,那么不太现实,所以核心就是抓主干逻辑,梳理清楚核心关键点,在看细节。

在这里插入图片描述


http://www.ppmy.cn/embedded/148029.html

相关文章

如何使用 Python 连接 SQLite 数据库?

SQLite是一种轻量级的嵌入式数据库&#xff0c;广泛应用于各种应用程序中。 Python提供了内置的sqlite3模块&#xff0c;使得连接和操作SQLite数据库变得非常简单。 下面我将详细介绍如何使用sqlite3模块来连接SQLite数据库&#xff0c;并提供一些实际开发中的建议和注意事项…

flask-admin+Flask-WTF 实现实现增删改查

背景&#xff1a; flask-adminflask-wtf在网上可以搜索到很多资料&#xff0c;但有价值的很少&#xff0c;或许是太简单&#xff0c;或者是很少人这么用&#xff0c;或者。。。&#xff0c;本文将作者近礼拜摸索到的一点经验分享出来&#xff0c;给自己做个记录。 材料&#…

tryhackme-Pre Security-HTTP in Detail(HTTP的详细内容)

任务一&#xff1a;What is HTTP(S)?&#xff08;什么是http&#xff08;s&#xff09;&#xff09; 1.What is HTTP? (HyperText Transfer Protocol)&#xff08;什么是 HTTP&#xff1f;&#xff08;超文本传输协议&#xff09;&#xff09; http是你查看网站的时候遵循的…

探索 Vue.js 组件开发:从基础到进阶的完整指南

引言 在现代前端开发中&#xff0c;Vue.js 凭借其易用性和强大的功能&#xff0c;成为了开发者钟爱的框架之一。其核心理念——组件化开发&#xff0c;不仅让代码更加模块化、可维护&#xff0c;还大大提高了开发效率。本文将从基础入手&#xff0c;详细探讨 Vue.js 组件开发的…

uniapp blob格式转换为video .mp4文件使用ffmpeg工具

前言 介绍一下这三种对象使用场景 您前端一旦涉及到文件或图片上传Q到服务器&#xff0c;就势必离不了 Blob/File /base64 三种主流的类型它们之间 互转 也成了常态 Blob - FileBlob -Base64Base64 - BlobFile-Base64Base64 _ File uniapp 上传文件 现在已获取到了blob格式的…

CH430N 插上电脑无反应

电路图&#xff0c;此处我用的是3.3V供电&#xff0c;现象就是插上USB,电脑没有反应。搜索也搜索不到 抄板请看自己是多少V供电 后来看到也有类似的 换了芯片后就好了。md新板子第一个芯片就是坏的&#xff0c;服了。

宠物管理系统(3):Controller类

模仿三层架构中的Controller编写的控制器&#xff08;但是这并非是真的&#xff09;&#xff0c;用于处理不同的情境。 MainController&#xff0c;用于控制整个主程序&#xff1a; package com.wzb.controller;import com.wzb.utils.menu.MainMenu; import com.wzb.utils.wai…

RabbitMQ消息可靠性保证机制7--可靠性分析-rabbitmq_tracing插件

rabbitmq_tracing插件 rabbitmq_tracing插件相当于Firehose的GUI版本&#xff0c;它同样能跟踪RabbitMQ中消息的注入流出情况。rabbitmq_tracing插件同样会对流入流出的消息进行封装&#xff0c;然后将封装后的消息日志存入相应的trace文件中。 # 开启插件 rabbitmq-plugins …