首页 > 编程语言 >【Spring事务】深入浅出Spring事务从原理到源码

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

时间:2024-12-22 17:56:49浏览次数:9  
标签:definition 事务 Spring txObject 源码 dataSource public user

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

一个事务的demo

@Configuration
@ComponentScan("com.qxlx.tx.annotation")
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public 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;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional
    public void register(User user) {
        userDao.save(user);
    }

}
@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public 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的基础上 创建事务代理对象,完成一系列的功能。源码内容本身是比较复杂和关系比较错乱,如果想每行源码都搞清楚,那么不太现实,所以核心就是抓主干逻辑,梳理清楚核心关键点,在看细节。

在这里插入图片描述

标签:definition,事务,Spring,txObject,源码,dataSource,public,user
From: https://blog.csdn.net/jia970426/article/details/144570316

相关文章

  • springboot毕设网上评教系统论文+程序+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着信息技术的高速发展,教育领域也在不断进行信息化变革。传统的评教方式往往依赖于纸质问卷等形式,存在诸多弊端,例如数据收集和统计效率低下、信......
  • springboot毕设理发店会员管理系统程序+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景在现代社会,理发店行业竞争日益激烈。随着人们生活水平和消费观念的转变,消费者对于理发服务的要求不仅仅局限于剪发本身,更包括个性化的服务体验、......
  • flask框架家庭智能理财管理毕设源码+论文
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、选题背景随着经济的发展和人们生活水平的提高,家庭理财变得日益重要。关于家庭理财管理的研究,现有研究主要以传统理财方式为主,如银行储蓄、股票......
  • springboot毕设 智慧医疗系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,智慧医疗系统已成为现代医疗体系中的重要组成部分。近年来,医疗资源的紧张与民众健康需求的日益增长之间的矛盾日益凸显,传统医......
  • springboot毕设 智能热度分析和自媒体推送平台 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展和社交媒体的广泛普及,自媒体已成为信息传播的重要渠道。每天,海量的文章、图片和视频等内容在各大自媒体平台上发布,用户面对......
  • springboot毕设 影视周边电商平台 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着影视产业的蓬勃发展,影视作品的热度不断攀升,带动了相关衍生品市场的快速增长。影视周边产品作为连接影视作品与观众的桥梁,不仅丰富了观众的文化消......
  • springboot毕设 月嫂服务管理系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着现代家庭对母婴护理需求的日益增长,月嫂服务已成为众多新手父母在迎接新生儿到来时的必备选择。然而,传统的月嫂服务模式往往依赖于口碑推荐或中介......
  • Java毕设项目:基于Springboot助农农产品销售平台系统设计与实现开题报告
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育、辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩,提供核心代码讲解,答辩指导。项目配有对应开发......
  • 【MySql】事务管理(下)
    ......
  • Springmvc进一步学习(springmvc-mybatis):跳转不经过视图解析器、controller如何保存数
    上一章的springmvc回顾内容:1.springmvc框架:主要作用:处理客户的请求,并处理数据,以及响应客户。2.springmvc搭建流程:  [1]引入springmvc依赖。spring-webmvc  [2]编写springmvc配置文件。   1.包扫描<context:component-scanbasepackage=""/>   ......