Spring 学习笔记之 @Transactinal实现原理

Spring 框架中的@Transactional注解是用来简化数据库事务管理的重要工具。下面详细介绍它实现数据库事务的原理。

核心原理概述

@Transactional主要借助 Spring 的 AOP(面向切面编程)机制来达成事务管理。当一个方法被@Transactional注解修饰时,Spring 会在该方法执行前后添加事务相关的处理逻辑,以保证方法内的数据库操作在一个统一的事务环境中进行。

具体实现步骤

1. 配置事务管理器

在 Spring 应用里,你得先配置一个事务管理器,这个管理器会和具体的数据源关联起来。例如,使用DataSourceTransactionManager管理基于 JDBC 的事务。以下是 Java 配置示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;

@Configuration
public class TransactionConfig {

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

2. 启用事务注解支持

在 Spring 配置类上添加@EnableTransactionManagement注解,以此开启@Transactional注解的功能。

import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class AppConfig {
    // 其他配置
}

@EnableTransactionManagement 注解的作用就是开启这种声明式事务管理功能。

注解属性

  • mode:用于指定 AOP 代理模式,有 AdviceMode.PROXY(默认值,使用 Spring AOP 代理)和 AdviceMode.ASPECTJ(使用 AspectJ 进行切面织入)两种可选值。
  • proxyTargetClass:仅在 mode 为 AdviceMode.PROXY 时有效,若为 true 则使用 CGLIB 代理,为 false 则使用 JDK 动态代理。
  • order:指定事务增强器的执行顺序。

工作流程

  • 导入配置类:@EnableTransactionManagement 注解借助 @Import 注解导入 TransactionManagementConfigurationSelector 类。

  • 配置类选择:TransactionManagementConfigurationSelector 会依据 AOP 代理模式来选择配置类。若使用 JDK 动态代理,就导入 ProxyTransactionManagementConfiguration 类;若使用 CGLIB 代理,则导入 AspectJTransactionManagementConfiguration 类。

  • 注册事务增强器:所选的配置类会注册 BeanFactoryTransactionAttributeSourceAdvisor 事务增强器。这个增强器会扫描带有 @Transactional 注解的方法,为其创建代理对象。

3. AOP 代理创建

当 Spring 容器启动时,它会扫描所有带有@Transactional注解的类和方法,然后借助 AOP 为这些类或方法创建代理对象。Spring 提供了两种代理方式:JDK 动态代理和 CGLIB 代理。

  • JDK 动态代理:若目标对象实现了接口,Spring 默认会使用 JDK 动态代理。
  • CGLIB 代理:若目标对象没有实现接口,Spring 会使用 CGLIB 代理。
     

4. 事务拦截器插入

在代理对象中,Spring 会插入一个事务拦截器(TransactionInterceptor)。这个拦截器会在目标方法执行前后进行事务管理。

@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
	...

	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

}

执行拦截器invoke方法的时候,主要业务逻辑在invokeWithTransaction方法里。

5. 事务拦截器工作流程

  • 方法调用:当调用被@Transactional注解修饰的方法时,实际上调用的是代理对象的方法。
  • 事务开启:事务拦截器会根据@Transactional注解的配置,从事务管理器获取一个事务,并开启这个事务。
  • 目标方法执行:在事务开启后,拦截器会调用目标对象的实际方法,执行数据库操作。
  • 事务提交或回滚
    • 正常情况:若目标方法正常执行完毕,没有抛出异常,事务拦截器会提交事务。
    • 异常情况:若目标方法抛出了异常,事务拦截器会根据@Transactional注解的rollbackFornoRollbackFor属性来判断是否需要回滚事务。若需要回滚,就会执行回滚操作。

invokeWithTransaction源代码如下,讲述了事务的具体执行流程。

PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (retVal != null && txAttr != null) {
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null) {
					if (retVal instanceof Future<?> future && future.isDone()) {
						try {
							future.get();
						}
						catch (ExecutionException ex) {
							Throwable cause = ex.getCause();
							Assert.state(cause != null, "Cause must not be null");
							if (txAttr.rollbackOn(cause)) {
								status.setRollbackOnly();
							}
						}
						catch (InterruptedException ex) {
							Thread.currentThread().interrupt();
						}
					}
					else if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
						// Set rollback-only in case of Vavr failure matching our rollback rules...
						retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
					}
				}
			}

			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

retVal = invocation.proceedWithInvocation(); 执行原始业务逻辑。

completeTransactionAfterThrowing(txInfo, ex); 处理碰到异常事务回滚。

commitTransactionAfterReturning(txInfo); 正常流程执行完成,提交事务。

示例代码

以下是一个简单的示例,展示如何使用@Transactional注解:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        // 可能会抛出异常的代码
        if (user.getName() == null) {
            throw new RuntimeException("用户名不能为空");
        }
    }
}

在上述示例中,createUser方法被@Transactional注解修饰。若方法执行过程中抛出RuntimeException,事务会回滚,user不会被保存到数据库。

总结

@Transactional注解通过 Spring 的 AOP 机制,在目标方法执行前后插入事务管理逻辑,从而实现了数据库事务的自动管理。它大大简化了事务处理的代码,提升了开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值