1 简述 + 源码跟踪入口
前两篇文章《spring源码】— spring-aop和spring事务前置处理方法》和《【spring事务源码学习】— 目标对象增强核心源码解读》讲解了spring事务核心后置处理器利用动态代理机制在目标对象创建+初始化过程中对其进行增强的核心源码。
同被spring-aop核心后置处理器代理增强的目标对象一样(对应文章:《【Spring - AOP】 — 目标方法调用流程核心源码解读》),当调用被spring事务增强的目标对象里的目标方法时,调用的肯定将不简简单单只是方法本身。本篇文章将主要探索一下被spring-事务核心后置处理器代理增强的目标对象在被调用时的具体执行流程。
源码的跟踪入口为从IOC容器获取目标对象 —> 并拿着目标对象调用目标方法。如下图所示:
2 目标方法调用流程核心源码解读
2.1 invoke(…) — 目标方法调用 + 调用结果返回流程的骨架
在图中1位置打断点,由于userServiceImpl对象为经spring事务代理增强后的对象 —> 因此进入该方法,进入的并不是userServiceImpl.addUserAndSalary(…)本身,而是JdkDynamicAopProxy类中的invoke方法,该方法是代理对象对目标对象增强的入口和目标方法调用+调用结果返回流程的模版代码,其核心源码如下:
所在类:JdkDynamicAopProxy
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
//获取当前真正调用的类 --- 其实这里的TargetSource是对真正调用的类一个封装对象,不能算真正的当前调用类
//如果是配了多数据源,这里就可以直接获取到到底用的哪个数据源了,可以用我的工程进行测试
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//-------------------------此处省略n行代码--------------
Object retVal;
//如果aop的exposeProxy设置的为true(将代理的目标对象暴露出来),这个if语句就能进去----这里我会再写一篇文章
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget(); // 从targetSource中取出当前真正调用的类
Class<?> targetClass = (target != null ? target.getClass() : null);
//根据事务通知和当前调用的方法+当前调用的类获取方法拦截链 --- AOP源码也用到了(责任链模式)不细讲
//这里获取到的方法拦截链其实就只有一个拦截器 ---> TransactionInterceptor
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//-----------一般不会走
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation... //拿着方法拦截链创建方法调用
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed(); // ---------进行真正的方法调用,并获取到当前方法具体的返回值------
}
// 省略n行代码 ---- 将获取到的返回值转型成目标方法的返回值类型等。。。。最后会将具体结果返回给调用者
}
注意:
获取到的拦截链其实就只包含一个事务拦截器 — TransactionInterceptor
。该拦截器注入IOC容器的原理可以参看我的另一篇文章《【spring事务源码学习】— spring事务核心组件创建过程》。
2.2 proceed方法 — 责任链+递归调用模式的精髓
跟踪retVal = invocation.proceed();
语句,进入到proceed方法的源码:
所在类:ReflectiveMethodInvocation
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// this.currentInterceptorIndex初始值为-1,interceptorsAndDynamicMethodMatchers即获取到的方法拦截链
// 第一次肯定不会进入该if语句块
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint(); //调用真正的业务方法
}
//获取到方法拦截器链中的第一个(++this.currentInterceptorIndex = 0)对象即 事务拦截器TransactionInterceptor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
//省略n行代码
}
else {
// 事务拦截器真正拦截目标方法的入口
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这块代码其实在《【Spring - AOP】 — 目标方法调用流程核心源码解读》那篇文章里就已经讲到过。这里用到了责任链+递归调用的设计模式,事务的情况下可能体会的不是很明显,但是在AOP情况下相信你一定可以非常明显的体会到其设计的精妙之处,有兴趣的可以看一下我那篇文章最后画的那个图。。。
2.3 invoke(this) 方法— 以事务方式调用目标方法的入口
跟进((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
,进入到如下源码:
所在类:TransactionInterceptor
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface. // 获取到目标方法
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//以事务的方式调用目标方法
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
2.4 invokeWithinTransaction — 事务方式调用目标方法的骨架 ★★
所在类:TransactionAspectSupport
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
//获取当前调用方法的事务属性,这块代码可以接上上篇文章介绍的2.4 - 2.8,
//其具体逻辑为: 如果缓存对象里有该方法的事务属性直接从缓存对象里拿,
//如果没有,对当前方法进行解析,如果解析到当前方法有事务属性,将解析结果放到缓存map对象里,并将事务属性拿回来
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//根据事务属性去拿事务管理器 ---> 这里拿到的就是我在《【spring事务源码学习】--- spring事务核心组件创建过程》
//那篇文章里配的DataSourceTransactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//获取当前执行方法的描述符,比如:com.nrsc.springstudy.c11_Transaction01.service.impl.UserServiceImpl.addUserAndSalary
//即当前方法对应的 ---> 全额限定名.方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//如果有必要,创建事务 ---> 这块逻辑其实挺复杂的,涉及到了事务传播行为等逻辑 ---> 会着重分析一下
TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.
//调用目标方法 ---> 从这里出去其实会进入到2.2中的proceed方法,到时就会直接进入第一个if语句,执行目标方法了
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception ===> 回滚事务 --- 不细究了
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo); // 清除事务信息 --- 不细究了
}
commitTransactionAfterReturning(txInfo); //提交事务 --- 不细究了
return retVal; // 将获取到的返回值进行返回
}
else {
//省略n行代码 ---- 编程式事务的逻辑,这里暂时不做研究
}
}
2.5 createTransactionIfNecessary — 获取事务信息对象的骨架
所在类:TransactionAspectSupport
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
//给当前事务属性一个名字,其实名字就是joinpointIdentification ---> 2.4有其含义
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//获取事务状态 --- 开启事务的入口
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//将当前方法、事务属性、joinpointIdentification和事务状态封装成一个事务信息对象并返回
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
2.6 getTransaction — 开启事务的核心代码 ★
所在类:AbstractPlatformTransactionManager
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
//获取当前方法的事务管理对象 --- 第一次进入该方法时有可能获取不到
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
//如果没有事务属性信息,就新建一个默认的
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
//如果存在当前事务的管理对象 --- 即当前方法存在于一个事务中
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 按照事务传播行为进行挂起、报错、新建事务等行为 --- 不细讲了
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//能走到这里说明当前方法还不存在事务
// Check definition settings for new transaction. //超时判断
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
//如果为事务的传播行为是MANDATORY走下面的分支
//MANDATORY :使用当前事务,如果当前没有事务就抛出异常,走到这里肯定没有事务,所以会抛出异常
//有兴趣的可以看我的另一篇文章《【spring事务前置知识】事务的传播行为》了解一下事务的传播行为
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//suspend方法是挂起事务,能走到这里说明还没开启事务,所以这里传入参数null,表示不挂起事务
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//创建一个默认的事务状态
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//真正的开启事务
doBegin(transaction, definition);
prepareSynchronization(status, definition); //将事务属性放到线程对象里
return status; //返回事务状态
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else { // 其它事务传播行为 --- 不细研究了
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
2.6.1 doGetTransaction — 获取事务管理对象
doGetTransaction方法的源码如下:
所在类:DataSourceTransactionManager
@Override
protected Object doGetTransaction() {
//新建一个事务对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//根据数据源去事务同步管理器对象TransactionSynchronizationManager中去获取连接持有者对象 --- 线程级别的
//第一次有可能获取不到,因为如果当前方法不处在其他方法的事务当中,走到这里,其实还没开启事务,
//也肯定没将数据库连接等放入到线程对象中
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//无论获取到还是没获取到都标记这个连接不是一个新的连接了
//其源码为:this.newConnectionHolder = newConnectionHolder;
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
(1) 这里看一下ConnectionHolder
类的一段注释:
/*
* Resource holder wrapping a JDBC {@link Connection}.
* {@link DataSourceTransactionManager} binds instances of this class
* to the thread, for a specific {@link javax.sql.DataSource}.
* /
从这段注释中可以看到ConnectionHolder对象其实是一个封装了数据库连接(和事务管理)的对象,且该对象是与线程是绑定在一起的。
(2)接着来TransactionSynchronizationManager
,如下图。其实它有一系列的ThreadLocal对象组成,这些ThreadLocal对象主要用来保存当前线程当前事务的属性。 — 事务传播行为的挂起和挂起后的恢复等都是借助此类来完成的。
(3)最后来看一下关于DataSourceTransactionObject
对象的注释
/**
* DataSource transaction object, representing a ConnectionHolder.
* Used as transaction object by DataSourceTransactionManager.
*/
翻译过来就是DataSourceTransactionObject对象代表一个ConnectionHolder,它被DataSourceTransactionManager用做事务对象。
说的更白一点就是DataSourceTransactionObject其实是真正的事务管理对象。
2.6.2 doBegin — 真正的开启事务
doBegin 源码如下:
所在类:DataSourceTransactionManager
/**
* This implementation sets the isolation level but ignores the timeout.
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//获取一个新的数据库连接对象
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
//将新的数据库连接对象封装成ConnectionHolder对象并set到DataSourceTransactionObject对象里
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//设置同步
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection(); //拿出新创建的数据库连接对象
//设置隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) { //如果是事务为自动提交
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false); //将事务的自动提交关闭,即开启事务
}
//准备事务连接并将事务激活
prepareTransactionalConnection(con, definition); //这块代码其实是判断是否为只读事务,如果是将其设置为只读事务
txObject.getConnectionHolder().setTransactionActive(true);
//判断事务超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
//将封装有数据库连接的ConnectionHolder对象绑定到线程对象里
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) { //出错的情况--- 不细研究了
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
2.6.2.1 prepareTransactionalConnection — 准备事务连接(处理只读事务逻辑)
prepareTransactionalConnection源码如下:
所在类:DataSourceTransactionManager
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();
}
}
}
2.6.3 prepareSynchronization — 将事务属性放入到线程对象里
所在类:AbstractPlatformTransactionManager
prepareSynchronization源码如下,其实就是将当前方法的事务属性放到TransactionSynchronizationManager所持有的线程对象里。
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();
}
}
3 总结
将spring事务目标方法调用流程的核心源码进行进一步归纳总结,得到下图: