Spring 事务解析

Spring 事务解析

什么是事务

事务是指数据库管理系统执行的一组操作,作为一个逻辑单元呈现,要么全部成功地执行,要么全部不执行。事务的目的是保证数据库在并发操作时的一致性和完整性。

数据库事务应满足的四个特性:

  • 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部执行成功,要么全部回滚到事务开始前的状态,没有中间状态。如果在事务执行过程中发生错误,所有已执行的操作都必须回滚,使数据库恢复到原始状态。
  • 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。这意味着事务在执行过程中对数据的修改必须符合预定义的规则,以保证数据库的一致性。
  • 隔离性(Isolation):事务的执行是相互隔离的,一个事务的执行不能影响其他事务的执行。隔离性确保每个事务在并发执行时都能像被串行执行一样,避免了并发操作导致的数据不一致问题。
  • 持久性(Durability):一旦事务提交,其所做的修改将永久保存在数据库中,即使发生系统故障或重新启动数据库系统,也能保证数据的持久性。

Spring 中的事务基本原理

  1. 首先从 @EnableTransactionManagement 注解开始
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 在开启事务的注解中,引入了 TransactionManagementConfigurationSelector 类
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	// 默认值为false。如果设置为true,则表示强制使用基于类(CGLIB)的代理而不是基于接口(JDK)的代理。当目标对象没有实现任何接口时,必须使用CGLIB代理。
	boolean proxyTargetClass() default false;
	
	// 默认值为AdviceMode.PROXY。
	// 表示使用哪种AOP代理模式,可选值为AdviceMode.PROXY和AdviceMode.ASPECTJ。
	// 当使用AdviceMode.PROXY时,Spring将创建基于JDK的动态代理或基于CGLIB的代理;
	// 当使用AdviceMode.ASPECTJ时,Spring将使用AspectJ编译器生成的字节码。
	AdviceMode mode() default AdviceMode.PROXY;
	
	// 默认值为Ordered.LOWEST_PRECEDENCE。表示定义切面的优先级,用于指定多个切面的执行顺序。值越小,优先级越高。
	int order() default Ordered.LOWEST_PRECEDENCE;
}
  1. 进入TransactionManagementConfigurationSelector类中,该类中的核心方法是 selectImports,该方法的返回值是数组,会将返回值注册到 Spring 容器中。
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				// 默认是PROXY
				// 可以发现,此处主要是往Spring中注入了两个bean,分别是AutoProxyRegistrar和ProxyTransactionManagementConfiguration
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				// 表示不用动态代理技术,用ASPECTJ技术,比较麻烦了
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}
  1. 解析AutoProxyRegistrar类,通过该方法的不断深入,会发现最终是往 Spring 中注入了 InfrastructureAdvisorAutoProxyCreator 类,该类的继承关系如下:
    在这里插入图片描述
    该类实现了 BeanPostProcessor 接口,在 Spring 的生命周期中,BeanPostProcessor 接口是用于初始化后操作的接口,在 Spring 进行扫描过程中,会扫描出该接口的所有实现类,并执行它的后置处理方法 postProcessAfterInitialization

  2. 解析 postProcessAfterInitialization 方法,该方法的实现并没有放在 InfrastructureAdvisorAutoProxyCreator 中,而是放在了 AbstractAutoProxyCreator 中去进行实现,若该 bean 可以被 Advisors 匹配,则会生成代理对象返回。


public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// 判断是否符合代理条件,若符合,执行代理
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}

		// advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aop
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}

		// 当前正在创建的Bean不用进行AOP,比如切面Bean
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象
		// 此处根据类以及类中的方法去匹配到Interceptor(也就是Advice),然后生成代理对象,代理对象在执行的时候,还会根据当前执行的方法去匹配
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			// advisedBeans记录了某个Bean已经进行过AOP了
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
  1. 回到第二步,解析 ProxyTransactionManagementConfiguration 类,该类中包含三个方法,其实就是创建了三个 bean。

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
			TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource);
		advisor.setAdvice(transactionInterceptor);
		if (this.enableTx != null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		// AnnotationTransactionAttributeSource中定义了一个Pointcut
		// 创建用于解析@Transactional注解的解析器。
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource);
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}
  1. 解析 TransactionInterceptor
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// TransactionAttribute就是@Transactional中的配置
		TransactionAttributeSource tas = getTransactionAttributeSource();
		// 获取@Transactional注解中的属性值
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

		// 返回Spring容器中类型为TransactionManager的Bean对象,这块使用到了模板模式
		final TransactionManager tm = determineTransactionManager(txAttr);

		// ReactiveTransactionManager用得少,并且它只是执行方式是响应式的,原理流程和普通的是一样的
		if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
			...
		}

		// 把tm强制转换为PlatformTransactionManager,所以我们在定义时得定义PlatformTransactionManager类型
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);

		// joinpoint的唯一标识,就是当前在执行的方法名字
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		// CallbackPreferringPlatformTransactionManager表示拥有回调功能的PlatformTransactionManager,也不常用
		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			// 创建事务,在这里面进行传播行为的处理
			// TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务
			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.
				// 执行下一个Interceptor或被代理对象中的方法
				retVal = invocation.proceedWithInvocation(); // test() sql
			}
			catch (Throwable ex) {
				// target invocation exception
				// 抛异常了,则回滚事务,或者
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
				
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null && txAttr != null) {
					retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
				}
			}

			// 提交事务
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			...
		}
	}
  1. 解析 transactionAdvisor 方法,该方法主要就是在 Spring 中获取切点和通知进行组装,然后在 Bean 的初始化后流程中,进行代理,处理事务。

以上便是 Spring 声明式事务的处理逻辑。

Spring 事务失效的基本场景

  1. 方法没有被代理:Spring 的事务管理是通过 AOP 实现的,只有被代理的方法才能被事务拦截器拦截并进行事务管理。如果一个类的方法没有被代理,那么事务管理将不会生效。如下场景:
public class Demo {
    @Transactional(rollbackFor = Exception.class)
    public void methodA() {
        System.out.println("执行数据库操作1...");
        System.out.println("执行数据库操作2...");
        System.out.println("执行数据库操作3...");
    }
}

Demo demo = new Demo();
demo.methodA();
  1. 不同类中的方法调用:事务默认是基于方法的,当一个事务方法A内部调用另一个类的方法B时,如果B方法没有标注@Transactional注解,那么它将在自己的独立事务中运行,而不受A方法的事务控制。如下场景:
@Service
class Demo {

    public void methodA() {
        methodB();
    }

    @Transactional(rollbackFor = Exception.class)
    public void methodB() {
        System.out.println("执行数据库操作1...");
        System.out.println("执行数据库操作2...");
        System.out.println("执行数据库操作3...");
    }
}

demo.methodA();
  1. 异常没有被捕获:事务默认只有在遇到未被捕获的 RuntimeException 或 Error 时才会回滚,而对于已捕获的异常,默认情况下是不会回滚事务的。如果捕获了异常但没有进行适当的处理或回滚,事务可能会失效。如下场景:
@Service
class Demo {

    @Transactional(rollbackFor = Exception.class)
    public void methodC() {
        try {
            int i = 1/0;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

demo.methodC();
  1. 异步方法:Spring 中的事务默认是不支持异步方法的。如果在异步方法上标注了 @Transactional 注解,事务将不会被应用。需要使用特殊的配置来支持事务在异步方法中的生效。如下场景:
@Service
class Demo {

    @Transactional(rollbackFor = Exception.class)
    @Async
    public void methodB() {
        System.out.println("执行数据库操作1...");
        System.out.println("执行数据库操作2...");
        System.out.println("执行数据库操作3...");
    }
  1. 事务传播行为设置不正确:事务的传播行为定义了事务方法与其他事务方法的相互关系。如果传播行为设置不正确,例如将一个事务方法嵌套在一个不支持事务的方法中,或者将一个事务方法调用标记为 REQUIRES_NEW(每次都创建新的事务),可能会导致事务失效。

  2. 事务超时:如果事务执行的时间超过了设置的事务超时时间,事务可能会被强制回滚,导致事务失效。

  3. 数据库不支持事务:某些数据库引擎可能不支持事务,或者配置不正确,导致事务失效。如 MyISAM、MEMORY,

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值