Spring Aop - Transactional注解原理深入剖析-第九篇

在研究Spring Aop Transaction之前,阅读了其他同学写的csdn的文章,这些文章对于事务切面执行的逻辑剖析的非常详细。但是我始终有一个疑问,就是我写了一个Service,是在什么阶段,通过Spring的哪个拓展点,生成的事物对象,翻阅了很多人的文章,始终没有找到满意答案,自己做了个Demo,从头到尾Debug了几遍,终于搞清楚了,瞬间豁然开朗。主要是从三个大的方面按照顺序去剖析:1.Spring tx包内的自定义标签解析。2.生成事物代理对象的过程。3.事物真正执行的过程。

实际例子

xml文件:

<import resource="classpath:mysql-datasource.xml"/>

<bean id="dataSourceLogService" class="com.yangt.risk.data.service.datasource.log.DataSourceLogServiceImpl"/>


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
</bean>

<!-- 使用annotation注解方式配置事务 -->
<tx:annotation-driven />
<tx:annotation-driven transaction-manager="transactionManager"/>

java代码

    @Override
    @Transactional(rollbackFor = { Exception.class })
    public void saveLog(DataSourceLogDTO sourceLogDTO) {
        RiskCallDatasourceLogDO datasourceLogDO = DataSourceLogConvert.dto2SourceLogDO(sourceLogDTO);
        if (Objects.isNull(datasourceLogDO)) {
            return;
        }
        riskCallDatasourceLogMapper.insert(datasourceLogDO);
        RiskCallDatasourceParamLogWithBLOBsDO paramLogWithBLOBsDO = DataSourceLogConvert.dto2ParamLogDO(sourceLogDTO, datasourceLogDO.getId());
        if (paramLogWithBLOBsDO == null) {
            throw new YTRiskException("dataSourceLog表未返回正确主键或者sourceLogDTO is null");
        }
        riskCallDatasourceParamLogMapper.insert(paramLogWithBLOBsDO);
    }

执行结果debug截图

截图说明:由于设置的lazy-init = false,所以在执行getBean方法之前,applicationContext内就已经将事务代理Bean初始化好了,下面会具体剖析代理Bean生成的整体流程。

前奏铺垫之Tx自定义标签

在spring-tx包内, META-INF下面spring.handler内http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

可以看到自定义标签解析类,org.springframework.transaction.config.TxNamespaceHandler,主要是有对三个标签的处理

    @Override
	public void init() {
		registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
	}

这里重点关注annotation-driven,这个标签类的解析,这里会有四个标签类解析注册。

  • InfrastructureAdvisorAutoProxyCreator(实现了 SmartInstantiationAwareBeanPostProcessor 接口,负责代理类的生成)

  • AnnotationTransactionAttributeSource(主要是用来存储一些事务相关的属性信息)

  • TransactionInterceptor(事务实现的核心拦截器,事务的回滚都是在其中完成)

  • BeanFactoryTransactionAttributeSourceAdvisor(增强器)

  • CompositeComponentDefinition(上述三种 bean 的组合)

事务代理类生成过程

以上是大体的流程图,需要对每一个步骤进行深入剖析:

第一步骤,回调方法被触发:

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization在Bean的初始化阶段,会被回调,主要原因是:AbstractAutoProxyCreator继承了org.springframework.beans.factory.config.BeanPostProcessor。回调方法被触发后,wrapIfNecessary会被执行,这一段逻辑很重要。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		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;
	}

第二步骤,getAdviceAndAdvisorsForBean,这里会获取到所有符合条件的advisor和advice

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

AbstractAdvisorAutoProxyCreator#findCandidateAdvisors找到所有实现Advisor接口的子类。这里会找到,BeanFactoryTransactionAttributeSourceAdvisor这个类的实例bean,注意:此bean是在前面的tx标签内注册的,已经注册到bean工厂内了。所以会拿到对应的实例bean。findAdvisorsThatCanApply是一个匹配的过程,具体的匹配逻辑如下:

for (Class<?> clazz : classes) {
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if ((introductionAwareMethodMatcher != null &&
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

具体匹配逻辑流程图:

最核心的matchs逻辑在:org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute

即寻找TransactionAttribute对象的过程。如果寻找到TransactionAttribute对象,即代表匹配到了合适的Advisor,那么getAdviceAndAdvisorsForBean方法则会返回数组,数组内部包含适配到的Advisor。

第三步骤,createProxy,构造代理对象

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

ProxyFactory对象内会填充targetSource以及advisor对象。

第四步骤,ProxyFactory#getProxy(java.lang.ClassLoader) 生成代理对象

这里会选择使用JdkDynamicAopProxy还是CglibAopProxy,如果是接口则使用JdkDynamicAopProxy,这里的内容https://blog.csdn.net/y510662669/article/details/106033377 有详细的介绍。

根据流程图上画的是九个步骤,后面总结了下,根据以上四个步骤,Transaction的代理类就真正生成了。

事务真正执行过程

由于此处使用的是接口方式,所以真正执行代理逻辑的切入点是:

org.springframework.aop.framework.JdkDynamicAopProxy#invoke

由前面的文章可知,真正代理的执行方法是:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

拿到实现MethodInterceptor或者Advice接口的实例。根据前面生成AopProxy代理类的逻辑可知,advised即:

BeanFactoryTransactionAttributeSourceAdvisor,那么就是从Advisor内拿到MethodInterceptor,为何能从Advisor内拿到MethodInterceptor呢?主要原因如下:

在org.springframework.transaction.config.TxNamespaceHandler解析xml,构造BeanDefinition时,已经将adviceName set进了BeanFactoryTransactionAttributeSourceAdvisor内,所以通过getAdvice方法,getAdvice方法即从BeanFactory工厂内根据adviceName去获取对应的TransactionInterceptor。

那么后面的逻辑,就水到渠成,只是执行TransactionInterceptor内的方法了。

以下是两个重要类的结构图:

通知器Advisor : org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor

切面Intercepter: org.springframework.transaction.interceptor.TransactionInterceptor

TransactionInterceptor 执行逻辑:

核心逻辑代码在:org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

1. 获取到TransactionAttribute、数据源管理对象、获取到要执行的切点信息。

2.判断是否是使用了TransactionCallback,就是使用transactionTemplate这种形式,默认@Transactional的注解,这个步骤会返回false,直接执行下面的逻辑。

3.创建一个TransactionInfo,这里又可以细分为两步。

1)拿到AbstractPlatformTransactionManager#getTransaction,获取到一个TransactionStatus,具体流程:

     1.1 会执行doBegin方法,doBegin方法首先会从dataSourceManager内拿到connection对象,然后利用connection对象设置一系列事务相关的属性。例如:把自动提交置为false,transactionActive置为true。这里会有一个判断,如果@Transactional 注解的readOnly=true,则会执行只读事物。这里重点看了下,什么是只读事务?即开启只读事务后,只能读到事务开启之前的修改,之后的修改感知不到。除非只读事务提交后,再次利用只读事物去读。适用场景:如果在一个查询中,需要查询数据库多个表,为了保证这几个表的数据的一致性和完整性,可以开启只读事务。注意:开启只读事务后,如果在只读事务内执行update,insert,delete操作,则会抛出异常。Spring事务开启只读事务的动作,即会执行一个sql : SET TRANSACTION READ ONLY。如下代码:

protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)
			throws SQLException {

		if (isEnforceReadOnly() && definition.isReadOnly()) {
			Statement stmt = con.createStatement();
			try {
				stmt.executeUpdate("SET TRANSACTION READ ONLY");
			}
			finally {
				stmt.close();
			}
		}
	}

   1.2 执行一系列的同步操作,把一些列变量绑定到一个单例类的ThreadLocal上,如下:

protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
		if (status.isNewSynchronization()) {
			TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
					definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
							definition.getIsolationLevel() : null);
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
			TransactionSynchronizationManager.initSynchronization();
		}
	}

2)org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo,执行此方法准备一个TransactionInfo,具体步骤如下:

   1.1 构造一个TransactionInfo对象,传入数据源管理对象、TransactionAttribute、切点信息对象。

   1.2 绑定到一个ThreadLocal。

4. 执行org.springframework.transaction.interceptor.TransactionAspectSupport.InvocationCallback#proceedWithInvocation这个方法,这个方法就是原始方法的回调。例如:此例子中,就是原始的saveLog方法,即执行此方法。

5.finally内的逻辑:cleanupTransactionInfo(txInfo); 把线程上绑定的txInfo置为老的txInfo,没太理解这部分的作用?

6.TransactionAspectSupport#commitTransactionAfterReturning方法执行,会通过txInfo拿到数据源管理对象,执行commit逻辑。具体代码:

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;
			try {
                // 拓展点:准备预提交
				prepareForCommit(status);
                // 拓展点:提交之前的trigger
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;
				boolean globalRollbackOnly = false;
				if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
					globalRollbackOnly = status.isGlobalRollbackOnly();
				}
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Releasing transaction savepoint");
					}
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
                    // 新事务则提交,即执行connection.commit方法
					doCommit(status);
				}
				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (globalRollbackOnly) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
				// can only be caused by doCommit
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
				// can only be caused by doCommit
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException ex) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, ex);
				throw ex;
			}
			catch (Error err) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, err);
				throw err;
			}

			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

可以看到:Spring事务执行阶段也是有很多拓展点的,方便我们使用,详细参考:https://www.jyoryo.com/archives/155.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值