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

相关推荐
龙少95432 小时前
【深入理解@EnableCaching】
java·后端·spring
啦啦右一8 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
荆州克莱11 小时前
mysql中局部变量_MySQL中变量的总结
spring boot·spring·spring cloud·css3·技术
zquwei11 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
火烧屁屁啦12 小时前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee
岁岁岁平安12 小时前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
北辰浮光12 小时前
[spring]XML配置文件标签
xml·spring
ZSYP-S13 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
路在脚下@14 小时前
Spring Boot @Conditional注解
java·spring boot·spring