从springboot整合spring事务到spring事务实现原理

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u010597819/article/details/88750491

启用spring事务

  1. @EnableTransactionManagement,没错只要增加启用事务管理器的注解即可使spring事务生效,当然还需要事务管理器来实现事务
  2. 进入“启用事务注解”,我们可以看到里面import引入了相关的配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
  1. 可以看到引入了TransactionManagementConfigurationSelector,该类的含义可以查看博主另一篇文章(springboot-spring-ioc-annotation源码学习)中有整个spring加载bean的详细过程,其中包含selector类的import,我就直接将它的作用,其实就是通过该注解的配置进行导入bean,默认的该注解模式为Proxy,也就是代理模式,会将ProxyTransactionManagementConfiguration与AutoProxyRegistrar注册至工厂中
  @Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {
						TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}
  1. AutoProxyRegistrar类根据注解配置将InfrastructureAdvisorAutoProxyCreator(SmartInstantiationAwareBeanPostProcessor)后置处理注册至工厂中
  2. ProxyTransactionManagementConfiguration类是一个配置类,主要提供BeanFactoryTransactionAttributeSourceAdvisor、transactionAttributeSource、transactionInterceptor三个bean的导入,该3个bean的具体作用后面会再具体详谈
  3. 至此启用事务的整个流程就圆满完成了

配置使用spring事务

  1. springboot中已经自动整合了spring事务的配置,也就是我们不再需要专门的去配置spring事务,直接使用即可,即直接使用@Transactional,然后我们来看看springboot是如何为我们自动配置的
  2. 那我们就要追到spring-boot-autoconfigure-1.5.19.RELEASE.jar里面的事务模块:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,然后我们竟然还有了一些意外收获哦
  3. 看到该自动配置类的内容,我们发现一个静态内部类EnableTransactionManagementConfiguration,我们发现里面自动为我们配置了启用事务,也就是说我们之前经常看到有人在论坛或者博客中提到的一个问题,就是为什么我没有配置@EnableTransactionManagement,但是事务依然是生效的,如果你的项目中使用到了springboot,这无疑就是一个可能性
  @Configuration
	@ConditionalOnBean(PlatformTransactionManager.class)
	@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
	public static class EnableTransactionManagementConfiguration {

		@Configuration
		@EnableTransactionManagement(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
		public static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration
		@EnableTransactionManagement(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
		public static class CglibAutoProxyConfiguration {

		}

	}
  1. 回到正题,我们可以看到该自动配置类已经为我们配置了事务管理器DataSourceTransactionManagerAutoConfiguration,可以看到里面默认使用的DataSourceTransactionManager实现
		@Bean
		@ConditionalOnMissingBean(PlatformTransactionManager.class)
		public DataSourceTransactionManager transactionManager(
				DataSourceProperties properties) {
			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
					this.dataSource);
			if (this.transactionManagerCustomizers != null) {
				this.transactionManagerCustomizers.customize(transactionManager);
			}
			return transactionManager;
		}
  1. 至此我们也结束了我们的springboot自动整合配置spring事务的流程,为自己打call吧_!!!

spring事务实现原理

从前面两节我们学会如何在springboot项目中使用spring事务,而且也简单了解了springboot如何为我们自动配置的事务,下面我们难免会好奇,spring的事务是如何实现的,当然很多人可能第一反应就是使用的springAOP呀,没错,是的,那么具体是如何织入的代码呢?让我进一步揭开它神秘的面纱吧,HIAHIAHIA。。。
如果看过spring源码的人应该都有所了解,一个bean的生命周期,如果不了解可以去上面博主提到的博文中查看详细的加载流程,当然博主还有精简总结版(Spring IOC加载总结

  1. 我们的突破点当然是在使用了事务的bean的初始化流程。可以从使用了事务的bean的初始化过程中寻找答案,可以通过debug断点的方式找到并定位到具体点
  2. 在此不再详述,spring事务的代码织入点是在bean初始化完成后的后置处理(postProcessAfterInitialization)中进行的,即InfrastructureAdvisorAutoProxyCreator,没错这个就是EnableTransactionManagement注解中AutoProxyRegistrar配置里面的registerBeanDefinitions注册至工厂的(AopConfigUtils.registerAutoProxyCreatorIfNecessary
  3. InfrastructureAdvisorAutoProxyCreator父类方法postProcessAfterInitialization,如果缓存中不包含bean,如果指定了必须则包装,如果存在对应的Advisor通知则创建代理
  4. InfrastructureAdvisorAutoProxyCreator父类方法getAdvicesAndAdvisorsForBean,查找合格的advisor通知findEligibleAdvisors
  5. 从工厂中查找所有Advisor实现bean,也就是包含之前配置的:BeanFactoryTransactionAttributeSourceAdvisor
  6. 根据beanName设置标志,标志为当前代理正在创建中,在完成后释放ProxyCreationContext.setCurrentProxiedBeanName(beanName);
  7. 根据代理的bean的class获取对应的通知列表AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
  8. 判断是否可以被应用AopUtils.canApply
  9. 如果是IntroductionAdvisor类型的通知,强转为IntroductionAdvisor类型获取ClassFilter判断是否匹配matches,如果返回true则说明匹配为合格的通知放入集合待返回
  10. 如果此时合格的通知不为空,说明hasIntroductions为true,根据hasIntroductions继续查找
  11. 如果通知为PointcutAdvisor类型(之前自动配置的就是该类型通知)
  12. 强转为PointcutAdvisors并获取Pointcut继续判断
  13. Pointcut获取ClassFilter判断是否匹配matches,匹配则继续
  14. Pointcut获取MethodMatcher,如果是MethodMatcher.TRUE实例则返回true,即匹配为合格的通知
  15. 如果MethodMatcher是IntroductionAwareMethodMatcher类型,则强转为IntroductionAwareMethodMatcher
  16. 遍历目标类的所有接口以及自身的方法,如果introductionAwareMethodMatcher判断方法匹配matches或者methodMatcher判断方法匹配matches返回true
  17. 返回合格通知列表并排序
  18. 如果存在通知则标识正在创建代理
  19. 创建代理createProxy
  20. 如果工厂是ConfigurableListableBeanFactory类型,则为代理bean定义中添加_ORIGINAL_TARGET_CLASS_ATTRIBUTE属性的值为目标class_
  21. 创建代理工厂ProxyFactory,并复制ProxyConfig配置(proxyTargetClass、optimize、exposeProxy、frozen、opaque)
  22. 如果proxyTargetClass配置为false,并且bean定义的_PRESERVE_TARGET_CLASS_ATTRIBUTE属性为false则执行_evaluateProxyInterfaces,否则重新设置proxyTargetClass为true
  23. 构建通知buildAdvisors
  24. 获取公共通知interceptorNames:Default is no common interceptors,默认为空
  25. 如果公共通知不为空并且设置为applyCommonInterceptorsFirst,则放入拦截最前面,否则放入后面
  26. 遍历所有拦截接口创建并包装对应的通知:advisorAdapterRegistry.wrap:如果是通知Advisor类型直接返回,如果是MethodInterceptor类型封装为DefaultPointcutAdvisor返回,如果是AdvisorAdapter类型调用适配类型的supportsAdvice类型判断是否支持,如果是则返回
  27. 返回通知列表
  28. 为ProxyFactory设置advisors、targetSource、freezeProxy,如果advisorsPreFiltered,则设置preFiltered为true
  29. 根据proxyClassLoader创建代理getProxy
  30. 创建AOP代理createAopProxy,通知监听AdvisedSupportListener.activated
  31. 获取aopProxyFactory工厂创建代理createAopProxy:默认为JdkDynamicAopProxy,如果目标类是不是接口并且不是Proxy代理,则使用ObjenesisCglibAopProxy
  32. 代理工厂创建代理JdkDynamicAopProxy.getProxy,InvocationHandler为当前实例JdkDynamicAopProxy
  33. 返回代理bean
  34. 至此学习了spring如何实现的事务以及aop,没错正是动态代理,下面我们继续看看jdk对spring事务的代理是如何包装的

spring事务jdk动态代理实现原理

  1. JdkDynamicAopProxy实现
  2. 如果是equals等方法直接返回目标调用
  3. 获取通知拦截器链chain:advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass),advised即ProxyConfig也就是ProxyFactory
  4. 如果拦截器链为空则直接反射调用(所以性能比cglib字节码方式实现的性能差一些)
  5. 如果拦截器链不为空则封装为ReflectiveMethodInvocation调用proceed
  6. 如果调用是调用链的最后节点(currentInterceptorIndex== this.interceptorsAndDynamicMethodMatchers.size() - 1)则直接反射调用invokeJoinpoint
  7. 否则从interceptorsAndDynamicMethodMatchers中获取currentInterceptorIndex下标实例调用并递增currentInterceptorIndex
  8. 如果是InterceptorAndDynamicMethodMatcher类型则获取methodMatcher判断是否匹配是则调用对应的interceptor.invoke(this)。如果不匹配则直接继续调用下个节点proceed
  9. 如果不是InterceptorAndDynamicMethodMatcher类型则强转为MethodInterceptor调用invoke(this)
  10. 我们再进一步看下spring事务的拦截器即之前提到的注解中自动配置的TransactionInterceptor
  11. 可以看到在调用之后会在回调中继续调用拦截器下个节点(即嵌套事务的情景),也就是入参this的递归调用,会获取递增currentInterceptorIndex后的对象
  12. 拦截器调用包含事务的方法invokeWithinTransaction
  13. 获取transactionAttributeSource事务属性源,即AnnotationTransactionAttributeSource,解析目标方法的事务属性getTransactionAttribute,例如:SpringTransactionAnnotationParser解析器解析对应的注解属性
  14. 根据事务属性配置获取对应的事务管理器(所以实际调用时会从缓存中获取事务管理器,如果不存在会根据配置获取对应的事务管理器,不是必要的等待事务管理器加载完成),所以配置中我们看到如果事务管理器不为空则设置,为空则不会有什么影响
  @Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor() {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}
  1. 属性不为空并且是CallbackPreferringPlatformTransactionManager类型事务管理器转为CallbackPreferringPlatformTransactionManager类型执行任务execute
  2. 默认自动配置的为DataSourceTransactionManager,不是CallbackPreferringPlatformTransactionManager类型。
  3. 尝试创建事务,如果需要createTransactionIfNecessary,从tm(事务管理器)中获取事务状态TransactionStatus
  4. 调用tm父类方法获取事务状态AbstractPlatformTransactionManager.getTransaction,根据事务定义(即事务属性)获取事务getTransaction
  5. doGetTransaction创建DataSourceTransactionObject(事务实例)与当前connectionHolder绑定
  6. 判断是否存在事务,判断transactionActive是否为true,存在则根据事务传递属性封装后返回事务状态handleExistingTransaction。
  7. 如果传递类型是_PROPAGATION_NEVER,则抛出异常:_IllegalTransactionStateException(“Existing transaction found for transaction marked with propagation ‘never’”)
  8. 如果传递类型是_PROPAGATION_NOT_SUPPORTED,则置空事务transaction,与后面的bind对应,即bind将当前事务挂起,以非事务方法执行,执行完成后的后置处理中再restore当前事务后进入下个拦截链节点_
  9. 如果传递类型是_PROPAGATION_REQUIRES_NEW,则新建事务_
  10. 如果传递类型是嵌套_PROPAGATION_NESTED_:如果允许save_point方式实现嵌套事务(嵌套事务spring事务管理器,通常使用JDBC3.0),则创建savepoint;否则使用begin commit/rollback方式实现(嵌套事务使用JTA实现)
  11. 其他传递类型,则标识为非新事务后返回(即newTransaction=false)
  12. 不存在事务,传递类型为_PROPAGATION_MANDATORY则抛出异常:_IllegalTransactionStateException( “No existing transaction found for transaction marked with propagation ‘mandatory’”)
  13. 不存在事务且传递性为_PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED其中之一;则新建事务_DefaultTransactionStatus,doBegin:设置自动提交为false等一系列操作;prepareSynchronization:准备事务同步管理器并绑定事务属性配置、初始化(创建新的事务同步TransactionSynchronization泛型的链表,主要用于在事务某些状态点得到同步回调动作),返回事务状态
  14. prepareTransactionInfo:根据事务状态封装为事务信息并准备事务信息,并与当前线程绑定,返回事务信息(其实就是与当前线程绑定bindToThread)
  15. 执行回调即递归调用下个拦截器节点(嵌套事务或实际方法等)
  16. cleanupTransactionInfo当前执行节点完成:恢复绑定上个节点事务信息restoreThreadLocalStatus
  17. 在拦截器链执行完成并返回返回值后处理提交commitTransactionAfterReturning
  18. 根据事务状态处理rollback或者commit
  19. 事务提交
  20. 如果有save_points,释放保存点(即提交保存点之后将保存点置空)
  21. 如果是新事务(说明所有拦截链节点均执行完成),直接获取连接句柄commit提交
  22. 返回目标调用方法返回值
  23. 一系列后置动作:清空标识以及若存在则通知回调

小结

  1. spring事务对于嵌套的实现有两种方式,一种是每次新增一个save point,一种是使用begin end方式
  2. 如果开启了校验已存在事务,每次获取已存在事务会校验其隔离级别是否与父隔离级别一致,如果不一致则抛出异常IllegalTransactionStateException,如果READONLY属性不一致则抛出同样抛出IllegalTransactionStateException异常
  3. TransactionSynchronizationManager管理同步通知,如果想要监听事务的各个状态节点可以实现同步接口并通过管理器注册即可

事务生命周期

事务生命周期

猜你喜欢

转载自blog.csdn.net/u010597819/article/details/88750491