SpringBoot中事务执行原理分析(一)

关联博文:
SpringBoot中事务执行原理分析(一)
SpringBoot中事务执行原理分析(二)
SpringBoot中事务执行原理分析(三)
SpringBoot中事务执行原理分析(四)
SpringBoot中事务执行原理分析(五)
SpringBoot中事务执行原理分析(六)
SpringBoot中事务执行原理分析补充篇
你认真研究过Spring中的@EnableTransactionManagement注解吗?

从本文开始我们开始分析事务执行原理,本文的环境是SpringBoot(+MybatisPlus),当然在SSM的环境下其本质也是一致的。

【1】两种环境下测试save

① 非事务环境下的save

如下所示,如果没有应用事务(比如事务注解或者xml配置或者编程式事务),那么通常在PreparedStatement执行后,数据就进入了数据库,即使下一行业务代码抛出了异常也不会导致回滚。

PreparedStatementHandler的update方法。

这里得到的PreparedStatement对象是个代理其真实对象中维护的connection的isAutoCommit=true,也就是自动提交。
在这里插入图片描述

ps.execute();方法将会触发入库动作的执行,数据将会插入数据库。

在这里插入代码片

插入完数据后依次返回,如下所示在SqlSessionInterceptor的invoke方法这里会执行sqlSession.commit(true);其最终会触发 transaction.commit();也就是提交事务。

在这里插入图片描述

② 有事务环境下的save

如下所示,我们在接口层给方法加上事务注解。

public interface SysAdviceService extends IService<SysAdvice> {
    @Transactional(rollbackFor = Exception.class)
    boolean testSave(SysAdvice sysAdvice);
}

这里得到的PreparedStatement对象是个代理其真实对象中维护的connection的isAutoCommit=false,也就是不自动提交

在这里插入图片描述

与上个环境不同的是当ps.execute();执行完,这里数据并没有插入。

在这里插入图片描述

在SqlSessionInterceptor的invoke方法中,这里也没有触发sqlSession.commit(true);

在这里插入图片描述
那么事务在什么时候提交的呢?数据在什么时候入库的呢?如下所示在TransactionAspectSupportinvokeWithinTransaction方法中commitTransactionAfterReturning(txInfo);将会执行事务提交将数据插入数据库。

在这里插入图片描述

③ 事务环境下抛出异常

如下所示,我们在service方法中加入异常代码。

@Override
public boolean testSave(SysAdvice sysAdvice) {
    adviceMapper.insert(sysAdvice);
    System.out.println(1/0);
    return false;
}

如下所示,这里TransactionAspectSupport的invokeWithinTransaction方法将会抛出异常。那么将不会触发commitTransactionAfterReturning(txInfo);进行事务提交插入数据。

在这里插入图片描述

继续往下跟踪,TransactionAspectSupport(实际当前对象是其子类TransactionInterceptor)的completeTransactionAfterThrowing方法中我们能够看到这里触发了回滚。

在这里插入图片描述

从上面的实例中我们看到了TransactionAspectSupport的身影,在有事务环境下该类来触发提交事务或者回滚的动作。那么TransactionAspectSupport是什么,从哪里来?

【2】TransactionAspectSupport

TransactionAspectSupport是事务切面的基础类,事务切面比如TransactionInterceptor。实际上TransactionInterceptor确实为TransactionAspectSupport的子类。

这使得底层Spring事务基础设施可以轻松地用于实现任何切面系统的切面。子类负责按正确的顺序调用此类中的方法。

这里 应用了策略设计模式。PlatformTransactionManagerReactiveTransactionManager 实现将执行实际的事务管理,而 TransactionAttributeSource(例如基于注解的事务)用于确定特定类或方法的事务定义。

我们继续看TransactionInterceptor。如下所示TransactionInterceptor继承自TransactionAspectSupport 实现了MethodInterceptor接口。

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
	//...
}

也就是说起在父类TransactionAspectSupport基础了,实现了MethodInterceptor接口,提供了invoke方法供其他地方调用。其实熟悉AOP的同学这里就能过联想到,实现了MethodInterceptor接口的大多被作为Advice使用。

如下所示当触发SysAdviceServiceImpl的testSave方法时,实际是触发了代理类然后交给了CglibAopProxy.DynamicAdvisedInterceptorDynamicAdvisedInterceptor方法。

在这里插入图片描述

如下所示最终会触发TransactionInterceptor的invoke方法。这里我们暂时跳过事务实现的详情,可以得出结论:SpringBoot下的事务执行原理是由代理实现的!

在这里插入图片描述

从前面我们也能够看到,在代理执行过程中,这里我们的advisor是BeanFactoryTransactionAttributeSourceAdvisor

那么什么是BeanFactoryTransactionAttributeSourceAdvisor,什么时候实例化的,如何对本文这里的SysAdviceServiceImpl进行了代理包装?

【3】BeanFactoryTransactionAttributeSourceAdvisor

看到Advisor第一反应就该是增强器/顾问器,就要联想到AOP、切面、切入点、增强通知、代理。

BeanFactoryTransactionAttributeSourceAdvisorTransactionAttributeSource 驱动,用于包含事务性方法的事务通知bean(比如你的方法上标注了@Transactional注解)。如下所示其继承自AbstractBeanFactoryPointcutAdvisor维护了transactionAttributeSourcepointcutadvice等成员信息。

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

	@Nullable
	private TransactionAttributeSource transactionAttributeSource;

	// 这个很有意思,是个final,且直接实例化赋值了
	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};
	//...
}

那么第一个问题,这个Advisor是在什么时候实例化的?

① BeanFactoryTransactionAttributeSourceAdvisor 的注册和实例化

如下所示,在配置类ProxyTransactionManagementConfiguration 中注册了BeanFactoryTransactionAttributeSourceAdvisorTransactionAttributeSource、以及TransactionInterceptor。并为transactionAdvisor设置advice属性,值为transactionInterceptor

@Configuration(proxyBeanMethods = false)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@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() {
		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;
	}

}

在这里插入图片描述

这个类总结来讲就是注册启用基于代理的注解驱动的事务管理所需的Spring基础结构bean。

那么第二个问题,ProxyTransactionManagementConfiguration 是如何注册实例化的?

② ProxyTransactionManagementConfiguration

如下所示,在TransactionManagementConfigurationSelectorselectImports方法中,“注册了”AutoProxyRegistrarProxyTransactionManagementConfiguration

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

// 注意这个selectImports方法哦 
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

TransactionManagementConfigurationSelector是一个ImportSelector,在SpringBoot自动配置原理解析一文中我们分析过,在应用上下文刷新过程中,会使用ConfigurationClassPostProcessor扫描并注册我们的配置类(和相关类)。

如下所示,我们自定义的MybatisPlusConfig 标注了@EnableTransactionManagement注解。

@EnableTransactionManagement
@MapperScan({"com.recommend.mapper"})
@Configuration
public class MybatisPlusConfig {
	//...
}

@EnableTransactionManagement注解注解导入了TransactionManagementConfigurationSelector,其selectImports又引入了AutoProxyRegistrarProxyTransactionManagementConfiguration
在这里插入图片描述
而在处理ProxyTransactionManagementConfiguration的过程中又会注入注册了BeanFactoryTransactionAttributeSourceAdvisorTransactionAttributeSource、以及TransactionInterceptor。并为transactionAdvisor设置advice属性,值为transactionInterceptor

这些Bean在ConfigurationClassPostProcessor中都会被注册了BeanDefinition,并在Spring中refresh分析之finishBeanFactoryInitialization方法详解被实例化。

③ 如何为我们的service进行代理包装?

其实换个说法就是如何为bean创建代理,这个具体过程我们在Spring AOP中如何为Bean创建代理?有过详细分析。我们这里简单看下如何为service包装代理。

如下所示,这里同样来到了AbstractAutoProxyCreator尝试为SysAdviceServiceImpl对象创建代理。

在这里插入图片描述

在wrapIfNecessary方法中会尝试获取当前bean的"拦截器"(没错,从业务概念上讲我们可以称之为拦截器),如果specificInterceptors不为空,则为当前bean实例创建代理。

如下所示,这里我们获取到了BeanFactoryTransactionAttributeSourceAdvisor,那么将会为我们的bean创建代理。

这里需要说明一点,首先从容器中获取到Advisor后,还会多进一步筛选。判断当前Advisor与目标类是否匹配。以BeanFactoryTransactionAttributeSourceAdvisor为例,这里会判断方法是否为public、方法上(当前类或者接口层)是否有事务注解信息。

关于Advisor与service匹配更多信息可以参考博文:SpringBoot中事务执行原理分析补充篇

在这里插入图片描述

DefaultAopProxyFactorycreateAopProxy方法中会进行判断决定是JdkDynamicAopProxy还是ObjenesisCglibAopProxy。本文这里TargetClass是实际的class com.recommend.service.impl.SysAdviceServiceImpl,将会触发cglib代理。
在这里插入图片描述

这里我们得到的代理对象如下所示:

在这里插入图片描述

④ 为什么是CGLIB代理?

在SSM环境下,如果我们定义了@EnableAspectJAutoProxy(proxyTargetClass = false),那么对于我们实现了接口的service来讲是触发JDK动态代理的。但是在SpringBoot 2.2.4.RELEASE版本下,由于AOP的自动配置,其强制将proxyTargetClass 设置为了true。

当我们尝试进行代理时,我们拿到的AnnotationAwareAspectJAutoProxyCreator对象中,proxyTargetClass 属性是true。也就是说假设在主启动类配置了@EnableAspectJAutoProxy(proxyTargetClass = false),SpringBoot会忽略你的false配置。

关于这个原理,可以参考AopAutoConfiguration,这个配置类中的CglibAutoProxyConfiguration默认采用proxyTargetClass = true效果。

在这里插入图片描述

Spring Boot是一个用于构建Java应用程序的开源框架,它简化了Spring应用程序的开发过程。事务是Spring Boot非常重要的一个特性,它可以确保数据库操作的一致性和完整性。下面是Spring Boot事务底层原理的介绍: Spring Boot使用了Spring框架事务管理器来实现事务功能。事务管理器是一个接口,它定义了一些方法来管理事务的开始、提交和回滚等操作。在Spring Boot,常用的事务管理器有两种:JpaTransactionManager和DataSourceTransactionManager。 JpaTransactionManager是用于管理JPA(Java Persistence API)事务事务管理器。它通过与JPA提供商(如Hibernate)进行交互,来实现对数据库的事务管理。 DataSourceTransactionManager是用于管理传统的关系型数据库(如MySQL、Oracle等)事务事务管理器。它通过与数据源进行交互,来实现对数据库的事务管理。 在Spring Boot,我们可以通过在方法上添加@Transactional注解来启用事务。当方法被调用时,Spring会自动创建一个事务,并在方法执行结束后根据方法的执行结果来决定是提交事务还是回滚事务事务的隔离级别、传播行为等属性可以通过@Transactional注解的属性来进行配置。例如,可以设置隔离级别为READ_COMMITTED,传播行为为REQUIRED等。 总结一下,Spring Boot的事务底层原理是通过事务管理器来管理事务的开始、提交和回滚等操作。我们可以通过@Transactional注解来启用事务,并通过注解的属性来配置事务的属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值