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

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

一个事务的demo

java 复制代码
@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);
    }

}
java 复制代码
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

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

}
java 复制代码
@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());
    }
}

测试类

java 复制代码
        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的方式引入外部类 然后去完成对应的功能。

java 复制代码
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	
	boolean proxyTargetClass() default false;
	
	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;	
}

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

java 复制代码
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 拦截器很关键,在运行具体方法的时候,使用的。

运行时的核心流程

java 复制代码
 userService.register(user);
java 复制代码
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
java 复制代码
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);


开启事务-createTransactionIfNecessary



getTransaction

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

java 复制代码
	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;
			}
	}
java 复制代码
	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

java 复制代码
	txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

事务回滚 completeTransactionAfterThrowing

原始方法 抛出异常一定回滚吗,

Exception及其子类 默认提交

RuntimeException及其子类 或者Error错误

java 复制代码
	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的类型。

java 复制代码
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

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

传播机制原理核心原理

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

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

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

总结

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

相关推荐
云烟成雨TD12 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-13 小时前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫13 小时前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD13 小时前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly15 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly15 小时前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh15 小时前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly15 小时前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode15 小时前
Spring 依赖注入方式全景解析
java·后端·spring