1. 程式人生 > >Spring @Transactional 事務回滾機制

Spring @Transactional 事務回滾機制

Srping 事務

    在Spring 的世界裡面我們一般使用@Transactional 註解在對應方法上面宣告為一個事務方法。
    但是在預設不寫@Transactional(rollbackFor = Exception.class)預設回滾RuntimeException
今天就希望通過原始碼的方式瞭解一下@Transactional的回滾機制。

Spring 原始碼解析

首先我們先編寫一個測試demo如下所示

@Service
public class TestService {

    @Autowired
    private ModelRepo modelRepo;
@Transactional(rollbackFor = {Exception.class,ClassNotFoundException.class,IOException.class} , noRollbackFor = FileNotFoundException.class) public void test() throws Throwable{ Model model = new Model(); modelRepo.save(model); throw new FileNotFoundException
(); } }

接著我們開始進入Debug

當我們的程式碼丟擲異常時Spring容器會建立一個RuleBasedTransactionAttribute物件,該物件就是我們那個方法上面@Transactional的一個描述。

//基於規則的事務屬性
public class RuleBasedTransactionAttribute 
    extends DefaultTransactionAttribute implements Serializable {

    //這就是那些 rollbackFor  noRollbackFor 異常的處理器
    @Nullable
	private
List<RollbackRuleAttribute> rollbackRules; ... } //這個就是異常回滾處理器的類 //回滾規則屬性 public class RollbackRuleAttribute implements Serializable{ /** * The {@link RollbackRuleAttribute rollback rule} for * {@link RuntimeException RuntimeExceptions}. */ //這裡預設就有個RuntimeException處理 //但是 @Transactional 預設回滾和它貌似沒有什麼關係 public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS = new RollbackRuleAttribute(RuntimeException.class); private final String exceptionName; //這裡就將待處理類名稱儲存起來 public RollbackRuleAttribute(Class<?> clazz) { Assert.notNull(clazz, "'clazz' cannot be null"); if (!Throwable.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException( "Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable"); } this.exceptionName = clazz.getName(); } //這兩個方法是很關鍵的是Spring核心處理邏輯 後續會繼續貼上程式碼 public int getDepth(Throwable ex) { return getDepth(ex.getClass(), 0); } private int getDepth(Class<?> exceptionClass, int depth) { //異常物件能夠被代理嗎? xxx.xxx.xxx.$xx 防止別人騷操作? if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... //找不到而且已經找到頭了 if (exceptionClass == Throwable.class) { return -1; } //兒子找不到,就找他爹咯 return getDepth(exceptionClass.getSuperclass(), depth + 1); } ... //略 }

在初始化一個異常回滾判定的 基於規則的事務屬性 RuleBasedTransactionAttribute物件之後就進入了TransactionAspectSupport(交易方面支援)的程式碼

public abstract class TransactionAspectSupport 
    implements BeanFactoryAware, InitializingBean {
    ...
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// If the transaction attribute is null, the method is non-transactional.
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		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 = null;
			try {
			    //核心程式碼
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				//執行Service的相關方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				//處理Exception判定回滾規則
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
		    ... //程式碼太多久不貼了	
		}
	}
	...
	
	//進入 completeTransactionAfterThrowing 方法看看
	
	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			//核心關鍵的處理邏輯來了     txInfo.transactionAttribute.rollbackOn(ex)
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
			    // 發話了我們不會回滾這個異常
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}
}

搞事搞事 我們進入 txInfo.transactionAttribute.rollbackOn(ex) 這個東西看看

第一層

public abstract class DelegatingTransactionAttribute 
        extends DelegatingTransactionDefinition
		implements TransactionAttribute, Serializable {
    ...
    
    @Override
	public boolean rollbackOn(Throwable ex) {
		return this.targetAttribute.rollbackOn(ex);
	}
	...
}

進入this.targetAttribute.rollbackOn(ex);
沒錯就是上面的RuleBasedTransactionAttribute

public class RuleBasedTransactionAttribute 
    extends DefaultTransactionAttribute implements Serializable {
    ...
    
    @Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;
        
        
        //因為這裡的處理器集合總是回滾的在前面,不回滾的在後面所以你認為不回滾一定無敵,那麼你就錯咯
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				int depth = rule.getDepth(ex);
				//騷還是Spring的人騷 這裡他麼是個就近原則
				// 所以理論上 rollbackFor  noRollbackFor 同一個類那麼應該是回滾的 可以試試
				//就近原則,一樣近走回滾
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}
	...
        
}

rule.getDepth(ex);

    public int getDepth(Throwable ex) {
		//重0開始計數
		return getDepth(ex.getClass(), 0);
	}
    
    //就是上面的rule.getDepth(ex); 獲取深度,結合上下文 就近原則了
    private int getDepth(Class<?> exceptionClass, int depth) {
		if (exceptionClass.getName().contains(this.exceptionName)) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass == Throwable.class) {
			return -1;
		}
		return getDepth(exceptionClass.getSuperclass(), depth + 1);
	}

有張圖片配合一下,可以看出裡面的處理器總是前面的是允許回滾的,後面的是不進行回滾的。

1534765415807

可以看出裡面的處理器總是前面的是允許回滾的,後面的是不進行回滾的。

以上就是@Transactional有配置 noRollbackFor ,rollbackFor 的執行流程

Spring @Transactional 無配置回滾執行

public class RuleBasedTransactionAttribute 
    extends DefaultTransactionAttribute 
    implements Serializable {

    ...
    
    @Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;

		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		//無配置時 進入了這裡了 找爹看看怎麼操作
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}
	
	...
}

看看爹怎麼操作的

public class DefaultTransactionAttribute 
    extends DefaultTransactionDefinition 
    implements TransactionAttribute {
    
    ...
    
    // 就這麼直白 裁判殺死了比賽  RuntimeException Error  回滾,其他再見
    @Override
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}
    ...
    
}