1.美圖
2.概述
3.案例
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void delete() throws RuntimeException {
System.out.println("==調用AccountService的delete方法\n");
jdbcTemplate.update(insert_sql);
throw new RuntimeException("==AccountService的delete方法手動拋出異常");
}
4.攔截器鏈調用回顧
攔截器鏈調用入口:
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*
* 調用JDK動態代理invoke方法
*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 1、處理equals方法,如果接口中沒有定義equals而在實現類中覆蓋了equals方法,那麼該equals方法不會被增強
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 2、處理hashCode方法,如果接口中沒有定義hashCode而在實現類中覆蓋了hashCode方法,那麼該hashCode方法不會被增強
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
// 3、如果目標對象是DecoratingProxy類型,則返回目標對象的最終對象類型
// DecoratingProxy接口只有一個getDecoratedClass方法,用於返回目標對象的最終對象類型
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// 4、如果目標對象是Advice類型,則直接使用反射進行調用
// opaque-->標記是否需要阻止通過該配置創建的代理對象轉換爲Advised類型,默認值爲false,表示代理對象可以被轉換爲Advised類型
// method.getDeclaringClass().isInterface()-->目標對象是接口
// method.getDeclaringClass().isAssignableFrom(Advised.class)-->
// 是用來判斷一個類Class1和另一個類Class2是否相同或者Class1類是不是Class2的父類。例如:Class1.isAssignableFrom(Class2)
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 5、解決目標對象內部自我調用無法實施切面增強,在這裏暴露代理
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();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// 6、獲取當前方法的攔截器鏈,並執行調用
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 檢測是否攔截器鏈是否爲空,如果攔截器鏈爲空,那麼直接通過反射調用目標對象的方法,避免創建MethodInvocation
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 創建MethodInvocation對象並調用proceed方法,攔截器鏈被封裝到了invocation中
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 調用攔截器鏈
retVal = invocation.proceed();
}
// 7、返回結果
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
參考: Spring : Spring Aop JDK動態代理調用過程
真正執行攔截器鏈調用
/**
* 調用攔截器鏈
*
* currentInterceptorIndex維護了一個計數器,該計數器從-1開始,當計數器值等於攔截方法長度減一時,
* 表名所有的增強方法已經被調用(但是不一定被真正執行),那麼此時調用連接點的方法,針對本例:即sayHello方法
*/
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 動態匹配增強
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 匹配成功則執行
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 匹配失敗則跳過並執行下一個攔截器
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
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);
}
}
當代碼執行到這裏的proceed方法裏,如果我們沒有配置其他的AOP增強,那麼第一個被執行的攔截器就是TransactionInterceptor
(事物攔截器)
5.TransactionInterceptor
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);
}
/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 1.準備工作
// 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);
// 獲取目標類全限定類名+連接點方法名 例如:com.lyc.cn.v2.day09.AccountServiceImpl.save
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 2.處理聲明式事物
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 2.2 創建事物(如果需要的話的,根據事物傳播特性而定)
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.3 繼續調用方法攔截器鏈,這裏一般將會調用目標類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 2.4 如果目標類方法拋出異常,則在此處理,例如:事物回滾
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 2.5 清除上一步創建的事物信息
cleanupTransactionInfo(txInfo);
}
// 2.6 調用成功完成後執行,但不是在異常被處理後執行。如果我們不創建事務,就什麼也不做。
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 3.處理編程式事物
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
這裏對聲明式事物和編程式事物分別做了不同的處理,我們主要看聲明式事物的實現過程,第一步的準備工作比較簡單,第二步是整個事物的核心所在。
- 創建事物(如果需要的話的,根據事物傳播特性而定)
- 繼續調用方法攔截器鏈,這裏一般將會調用目標類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
- 如果目標類方法拋出異常,則在此處理,例如:事物回滾
- 清除上一步創建的事物信息
- 調用成功完成後執行commitTransactionAfterReturning方法,但不是在異常被處理後執行。如果我們不創建事務,就什麼也不做。
這裏主要看這句話
2.2 創建事物(如果需要的話的,根據事物傳播特性而定)
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr,
final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
// 1. 如果沒有指定名稱,則應用方法標識作爲事務名稱。
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 2.獲取TransactionStatus對象
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");
}
}
}
// 3.創建TransactionInfo對象
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
最重要的是TransactionStatus對象的獲取過程,以及創建TransactionInfo對象
6.獲取TransactionStatus對象,即Spring事物創建過程
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// 1.獲取當前事物對象(如果當前已經存在了事物)
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
// 如果TransactionDefinition爲空,默認創建DefaultTransactionDefinition對象
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
// 2.如果當前已經存在事物
// 重點:
// 如果當前已經存在啓動的事物,則根據本次要新建的事物傳播特性進行評估,以決定對新事物的後續處理
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
System.out.println("==當前已經存在事物:"+TransactionSynchronizationManager.getCurrentTransactionName()
+". 新事物傳播特性爲" + definition.getPropagationBehavior()+"\n");
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 3.如果當前不存在事物
// Check definition settings for new transaction.
// 3.1 如果事物定義的超時時間,小於默認的超時時間,拋出異常,TransactionDefinition.TIMEOUT_DEFAULT --> -1
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 3.2 如果當前事物特性爲PROPAGATION_MANDATORY,則拋出異常(因爲當前事物還沒創建結束並開啓...)
// PROPAGATION_MANDATORY --> 使用當前事物,如果當前沒有事物,則拋出異常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 3.3 如果事物傳播特性爲以下三種,則創建新的事物:
// PROPAGATION_REQUIRED --> 如果當前沒有事物,則新建一個事物;如果已經存在一個事物,則加入到這個事物中。
// PROPAGATION_REQUIRES_NEW --> 新建事物,如果當前已經存在事物,則掛起當前事物。
// PROPAGATION_NESTED --> 如果當前存在事物,則在嵌套事物內執行;如果當前沒有事物,則與PROPAGATION_REQUIRED傳播特性相同
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED
|| definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW
|| definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
// 從日誌打印,也可以看見當前要創建一個名爲definition.getName()的新事物了...
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創建DefaultTransactionStatus對象實例
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;
}
}
// 3.4 對於其他的三種傳播特性,無需開啓新的事物
// PROPAGATION_SUPPORTS --> 支持當前事物,如果當前沒有事物,則以非事物方式執行
// PROPAGATION_NOT_SUPPORTED --> 以非事物方式執行,如果當前存在事物,則掛起當前事物
// PROPAGATION_NEVER --> 以非事物方式執行,如果當前存在事物,則拋出異常
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);
}
}
該方法可以說是Spring事物最最關鍵、最最核心的方法了。對於這裏比較重要的的方法,逐個分析。。。
獲取當前事物對象(如果當前已經存在了事物)。對於不同的事物管理器,獲取的方法也是不同的,本例使用的是DataSourceTransactionManager:
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允許使用保存點,是否允許使用保存點會在具體的事物管理器的構造方法中進行初始化
/**
* 例如:
* 針對本例分析的DataSourceTransactionManager,會在其構造方法中調動setNestedTransactionAllowed(true)方法,
* 設置允許使用保存點
*/
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 從當前線程中獲取ConnectionHolder對象
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
繼續分析,如果當前已經存在事物,則會涉及到事物嵌套處理(留在後面分析);如果當前不存在事物,則根據指定的事物傳播特性去分別作出處理即可。當然這裏還會做一些預處理工作,例如超時時間處理。
ry {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創建DefaultTransactionStatus對象實例
DefaultTransactionStatus status = newTransactionStatus(
definition,
transaction,
true,
newSynchronization,
debugEnabled,
suspendedResources);
// 開啓事物
doBegin(transaction, definition);
// 初始化事務同步。
prepareSynchronization(status, definition);
return status;
}
7.創建TransactionInfo對象
TransactionInfo持有了transactionManager、
transactionAttribute、joinpointIdentification、transactionStatus、oldTransactionInfo等信息。
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr,
String joinpointIdentification,
@Nullable TransactionStatus status) {
// 創建TransactionInfo對象
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
// 如果事物標籤不爲空,則將TransactionStatus對象賦予TransactionInfo
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
} else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled()) {
logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");
}
}
// We always bind the TransactionInfo to the thread, even if we didn't create
// a new transaction here. This guarantees that the TransactionInfo stack
// will be managed correctly even if no transaction was created by this aspect.
// 這裏:不論是否開啓了新的事物,都將TransactionInfo綁定到當前線程,以保證TransactionInfo堆棧的完整性。
txInfo.bindToThread();
return txInfo;
}
基本上就是簡單的賦值操作,並在最後將創建的TransactionInfo綁定到當前線程。注意,在綁定過程中,當前事物信息是會獲取已有的事物信息並一起綁定到transactionInfoHolder變量的。這樣就可能會形成一個事物鏈。
rivate void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
其中transactionInfoHolder是個ThreadLocal類型的變量:
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<>("Current aspect-driven transaction");