Spring中的事物源码解析

前言

在Spring中,使用事物的方式基本上都是通过声明@Transactional来完成的。

xml方式

在xml的IOC容器中<tx:annotation-driven/>开启Spring的声明式事物支持。

Spring事物xml标签的解析由TxNamespaceHandler来处理。

<tx:annotation-driven/>该标签的解析由AnnotationDrivenBeanDefinitionParser来解析处理。

Spring声明式事物涉及三个核心类:

  • BeanFactoryTransactionAttributeSourceAdvisor:
    在bean的初始化中,有一步populateBean,为该bean设置属性对象。
    属性是从哪里赋值的呢?
    AnnotationDrivenBeanDefinitionParser解析该xml标签的时候,通过代码添加的。

    /**
    	 * Parses the {@code <tx:annotation-driven/>} tag. Will
    	 * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
    	 * with the container as necessary.
    	 */
    	@Override
    	public BeanDefinition parse(Element element, ParserContext parserContext) {
    		registerTransactionalEventListenerFactory(parserContext);
    		String mode = element.getAttribute("mode");
    		if ("aspectj".equals(mode)) {
    			// mode="aspectj"
    			registerTransactionAspect(element, parserContext);
    		}
    		else {
    			// mode="proxy"
    			// 默认走这里,为该beanDefinition添加属性值,以便创建该bean的时候
    			// 其field域的属性能被赋值。
    			AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
    		}
    		return null;
    	}
    	
    

    populateBean()的该对象的属性值的时候,触发AnnotationTransactionAttributeSource对象和TransactionInterceptor的创建。即transactionAttributeSource属性adviceBeanName属性

  • AnnotationTransactionAttributeSource:
    AnnotationTransactionAttributeSource(true)决定了@Transactional只能应用在public方法中。
    值为true,代表@Transanctional只能应用在public方法中,且是基于AOP代理的
    值为false,代表@Transactional可以应用在protected or private方法中,且是基于AspectJ代理的

  • TransactionInterceptor:

  • DataSourceTransactionManager:

注解的方式

通过@EnableTransactionManagement来开启Spring事物的声明式管理,这个注解引入TransactionManagementConfigurationSelector

	/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {
				// 引入SpringAOP处理,即AnnotationAwareAspectJAutoProxyCreator
				AutoProxyRegistrar.class.getName(),
				// 事物配置类,即开启声明式事物所需的三个核心对象
				ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

该注解的解析是由ConfigurationClassPostProcessor来处理成可供IOC容器生成bean的BeanDefinition。【processImports阶段来处理该ImportSelector

AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor通过postProcessBeforeInstantiation来触发Spring声明式事物的三个核心类的Bean对象创建(findAdvisorBeans())。在这个过程中,触发对配置类ProxyTransactionManagementConfiguration的创建。

通过ProxyTransactionManagementConfiguration的创建来看ImportAware的作用?

initializationBean阶段中交由ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor处理ImportAware接口。有什么作用呢?获取该bean的注解元数据。

	// ImportAwareBeanPostProcessor 获取一个bean的注解元数据
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		if (bean instanceof ImportAware) {
			ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
			AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
			if (importingClass != null) {
				 // 设置注解元数据
				((ImportAware) bean).setImportMetadata(importingClass);
			}
		}
		return bean;
	}
	
	// 为ProxyTransactionManagementConfiguration设置注解元数据
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		this.enableTx = AnnotationAttributes.fromMap(
				importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
		if (this.enableTx == null) {
			throw new IllegalArgumentException(
					"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
		}
	}

代理对象

代理对象的生成是在bean初始化的initializationBean阶段,即advisedBeans缓存,防止重复代理bean对象(@Aspect注解的类不会被代理)。

    // 在doCreateBean之前,接口代理处理,会给advisedBeans赋值
	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
		// 这里,在AbstractAutoProxyCreator中,以下情况不能代理
		// 情况一:该类角色是基础设施类或者用切面注解,isInfrastructureClass(beanClass)
		// 情况二:shouldSkip,是否应该跳过代理
		// 是这两种情况,不能AOP代理,用advisedBeans存放判断结果
		// this.advisedBeans.put(cacheKey, Boolean.FALSE); return null;
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	}

生成的代理对象只有一个,对于自定义AOP跟@Transactional同切一个方法,也只对该方法所在类生成一个代理对象,所不同的是自定义的AOP切面对应的是InstantiationModelAwarePointcutAdvisorBeanFactoryTransactionAttributeSourceAdvisor这两个Advisor进行针对性切面匹配处理。

@Aspect进行自定义切面的,防止对方法的重复切逻辑匹配判断处理, 用shadowMatchCache进行缓存。
@Transactional进行事物切面的,防止对方法的重复切逻辑匹配判断处理, 用attributeCache进行缓存。【因为,匹配的过程,有关类下的所有方法进行匹配,且当执行该事物方法时,会重新再通过attributeCache获取TransactionAttribute。】

为什么要有这两个缓存?
因为,只要开启Aop和事物,那么在IOC容器初始化的bean的创建中,总会有Bean后置处理器AnnotationAwareAspectJAutoProxyCreator对该bean进行处理。那么,如果该bean需要代理,就会每次都走该处理器的父类AbstractAutoProxyCreatorwrapIfNecessary()方法。

	/**
	 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
	 * @param bean the raw bean instance
	 * @param beanName the name of the bean
	 * @param cacheKey the cache key for metadata access
	 * @return a proxy wrapping the bean, or the raw bean instance as-is
	 */
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 通过缓存的advisedBean先判断是否代理
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 基础设施类或者Aop类以及shouldSkip不代理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// 是否有针对该bean代理处理的Advisor
		// 这个获取,就是防止重复处理,用缓存进行
		// 如果是AOP,用shadowCache;如果是Transaction,用attributeCache缓存
		// 该方法对应的TransactionAttribute
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			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;
	}

这一过程发生在findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName)阶段中。

@Aspect@Transactional都是被AnnotationAwareAspectJAutoProxyCreator来处理的。而@Async是被AsyncAnnotationBeanPostProcessor所来处理的。
完整分析可以参考IOC容器初始化之AOP内部机制

事物执行

在执行事物方法(被切方法)时,防止重复进入创建方法执行器并处理,用methodCache方法缓存,缓存该方法的拦截器或者Advise链(是List<Object>),因为该方法会被不同切面处理,比如这个场景:自定义的AOP切这个方法和用 @Transactional切这个方法。同时,这个场景需要注意,自定义的切面如果跟声明式事物同切一个方法时,一定要注意,自定义切面的通知方法,显示抛出异常,否则,异常被吃掉,导致声明式事物不起作用。

会进入CglibAopProxy的intercept方法【方法拦截】。

获取到该方法的所有的拦截器InterceptorAdvice

	// 1. 获取AOP中所有的Advisor
	// 2. 从所有的Advisor中,筛选出它们的匹配器能匹配到该方法的MethodInterceptor
	// 在这个matches的过程中会用到TransactionAttribute,因此是从缓存中获取的。
	List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

创建CglibMethodInvocation方法执行器进行proceed()处理。

进入TransactionInterceptor的invoke处理,即在事物的情形下执行:
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);

1.获取事务属性源TransactionAttributeSource
	怎么来的?即是TransactionInterceptor中的属性值。
	在创建TransactionInterceptor对象的时候赋值。
		=> ProxyTransactionManagementConfiguration
		
2.从TransactionAttributeSource中获取事务属性TransactionAttribute。
	这里是从缓存中获取的,第一次创建是在创建事物方法所在的类的代理对象的初始化bean阶段,
	用AnnotationAwareAspectJAutoProxyCreator创建代理对象的时候,将@Transactional
	转换成TransactionAttribute。
	
3.从IOC容器中获取事务管理器TransactionManager【DataSourceTransactionManager】。
4.将事物管理器对象强转及获取事物方法信息(methodJoinPointInfo,以此作为事物名【默认】)5.根据事物属性,事务管理器,事物方法信息创建事物【createTransactionIfNecessary】
	(这里,注意一下命名,获取的是事物信息TransactionInfo,但是方法命名却是Transaction,
	简化方法的命名,不是根据返回值命名,而是根据语义命名。)
	
	5.1 根据事物属性从事务管理器中获取事物【TransactionStatus】
	(这里的方法名同上,也是没有命名为getTransactionStatus,而是getTransaction)
		
		5.1.1 真正获取事物(方法命名,doGetTransaction,且这是一个模板方法,实际实现,
		交由具体的事务管理器实现,比如DataSourceTransactionManager)
			创建一个事物对象,DataSourceTransactionObject
			给对象设定相应属性值,比如,嵌套事物,Jdbc连接
			Jdbc连接是怎么获取的?
			从事物同步管理器中获取:
			TransactionSynchronizationManager.getResource(obtainDataSource())
			事物同步管理器,通过NameThreadLocal将线程与事物相关信息进行了绑定,通过
			ThreadLocal,我们也不难看出,Spring的事物是本地事物。

		5.1.2 已经存在事物【场景:嵌套事物】
			判断事物存在的方法:
				就是判断新建事物(事物对象)中是否含有Jdbc连接(Holder)或者Jdbc连接
				(Holder)中的事物活跃
				
			处理存在的事物:handleExistingTransaction,依照事物的传播机制分场景处理

		5.1.3 当前不存在事物,依照事物的传播机制分场景处理
			不支持这种传播机制:TransactionDefinition.PROPAGATION_MANDATORY
			
			该事物方法定义的传播行为是以下三种:
				TransactionDefinition.PROPAGATION_REQUIRED
				TransactionDefinition.PROPAGATION_REQUIRES_NEW
				TransactionDefinition.PROPAGATION_NESTED
			挂起事物:suspend(null)
			
			开始事务:startTransaction
				创建事物状态TransactionStatus
				实际开始事务:doBegin(方法命名,没有起名doBeginTransaction,
				因为语境已是开始事物阶段)
				
					事物对象中不存在连接Holder或者连接没有到事物同步阶段
						从数据源中获取连接对象,为事物对象设置连接对象
					为事物对象设置事物同步标志true,到事物同步阶段
					为事物对象设置隔离级别,只读标识
					Jdbc连接自动提交:
						设置为非自动提交(因此,Spring事物都是非自动提交方式)
					准备事物连接:prepareTransactionalConnection
						事物是否只读
					为事物对象的连接Holder设置事物活跃标志,true
					事物对象是新创建的,绑定连接到当前线程ThreadLocal:
						TransactionSynchronizationManager.bindResource(
							obtainDataSource(), 
							txObject.getConnectionHolder());
					在整个doBegin阶段中抛异常的话:
						事物对象是新创建的:
							释放Jdbc连接
							为该事物对象的connectionHolder置空
				准备同步事物,绑定到线程:prepareSynchronization
			开始事物这个阶段抛异常:
				恢复挂起的事物资源:
					resume(null, suspendedResources);
	
	5.2 准备事物信息:prepareTransactionInfo
			将事物信息绑定到当前线程:
			原理的事物信息,赋值给oldTransactionInfo
6. 继续处理,多个切点,切的是同一个方法:
	invocation.proceedWithInvocation()
	最终,会再次进入ReflectiveMethodInvocation的proceed方法
	判断,事物方法的拦截器链执行完,执行实际方法invokeJoinPoint(被声明式事物注解的方法)
7.  执行被声明式事物注解的方法
	执行jdbc的方法的时候都没去提交。
8. 当执行完清除事物信息的时候cleanupTransactionInfo,提交事物:
	commitTransactionAfterReturning(txInfo);	
	
	通过事务管理器提交事物状态:commit
	
	是否回滚
	处理提交:processCommit
		提交前的准备:prepareForCommit
		提交前触发回调:triggerBeforeCommit
		事物完成前触发回调:triggerBeforeCompletion
		根据事物状态属性执行不同动作:
			有保存点savePoint,释放
			事物状态是一个新事物的状态,实际提交事物状态:doCommit(status)
	    提交事物之后触发回调:triggerAfterCommit
	    事物完成后触发回调:triggerAfterCompletion
	    	清除事物同步器
	    事物完成后清除:cleanupAfterCompletion
	    	事物状态的完成属性置为true
	    	事物同步器清除:TransactionSynchronizationManager.clear();
	    	完成之后实际清除事物:doCleanupAfterCompletion
	    		将数据源与当前线程解绑:		
					TransactionSynchronizationManager.unbindResource(obtainDataSource());
				重置Jdbc连接为自动提交
				重置事物之后的连接:
					DataSourceUtils.resetConnectionAfterTransaction(
					con, txObject.getPreviousIsolationLevel(), 
					txObject.isReadOnly());
					释放连接
					清除事物对象中的连接Holder
9.一次事物方法的执行已经完成
									
						
					
					
			
			
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值