spring源碼閱讀--@Transactional實現(xiàn)原理講解
@Transactional注解簡介
@Transactional是spring中聲明式事務(wù)管理的注解配置方式,相信這個注解的作用大家都很清楚。
@Transactional注解可以幫助我們把事務(wù)開啟、提交或者回滾的操作,通過aop的方式進行管理。
通過@Transactional注解就能讓spring為我們管理事務(wù),免去了重復(fù)的事務(wù)管理邏輯,減少對業(yè)務(wù)代碼的侵入,使我們開發(fā)人員能夠?qū)W⒂跇I(yè)務(wù)層面開發(fā)。

我們知道實現(xiàn)@Transactional原理是基于spring aop,aop又是動態(tài)代理模式的實現(xiàn),通過對源碼的閱讀,總結(jié)出下面的步驟來了解實際中,在spring 是如何利用aop來實現(xiàn)@Transactional的功能的。如果對spring的aop實現(xiàn)原理不了解,可以看aop實現(xiàn)原理分析。
spring中聲明式事務(wù)實現(xiàn)原理猜想
- 首先,對于spring中aop實現(xiàn)原理有了解的話,應(yīng)該知道想要對一個方法進行代理的話,肯定需要定義切點。在@Transactional的實現(xiàn)中,同樣如此,spring為我們定義了以 @Transactional 注解為植入點的切點,這樣才能知道@Transactional注解標注的方法需要被代理。
- 有了切面定義之后,在spring的bean的初始化過程中,就需要對實例化的bean進行代理,并且生成代理對象。
- 生成代理對象的代理邏輯中,進行方法調(diào)用時,需要先獲取切面邏輯,@Transactional注解的切面邏輯類似于@Around,在spring中是實現(xiàn)一種類似代理邏輯。

@Transactional作用
根據(jù)上面的原理猜想,下面簡單介紹每個步驟的源碼以進行驗證。
首先是@Transactional,作用是定義代理植入點。【aop實現(xiàn)原理分析】中,分析知道代理對象創(chuàng)建的通過BeanPostProcessor的實現(xiàn)類AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInstantiation方法來實現(xiàn)個,如果需要進行代理,那么在這個方法就會返回一個代理對象給容器,同時判斷植入點也是在這個方法中。
那么下面開始分析,在配置好注解驅(qū)動方式的事務(wù)管理之后,spring會在ioc容器創(chuàng)建一個BeanFactoryTransactionAttributeSourceAdvisor實例,這個實例可以看作是一個切點,在判斷一個bean在初始化過程中是否需要創(chuàng)建代理對象,都需要驗證一次BeanFactoryTransactionAttributeSourceAdvisor是否是適用這個bean的切點。如果是,就需要創(chuàng)建代理對象,并且把BeanFactoryTransactionAttributeSourceAdvisor實例注入到代理對象中。
其中【aop實現(xiàn)原理分析】知道在AopUtils#findAdvisorsThatCanApply中判斷切面是否適用當前bean,可以在這個地方斷點分析調(diào)用堆棧,AopUtils#findAdvisorsThatCanApply一致調(diào)用,最終通過以下代碼判斷是否適用切點。
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass)
這里可以根據(jù)參數(shù)打上條件斷點進行調(diào)試分析調(diào)用棧,targetClass就是目標class
…一系列調(diào)用 最終
SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
//這里就是分析Method是否被@Transactional注解標注,有的話,不用說BeanFactoryTransactionAttributeSourceAdvisor適配當前bean,進行代理,并且注入切點
//BeanFactoryTransactionAttributeSourceAdvisor
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
上面就是判斷是否需要根據(jù)@Transactional進行代理對象創(chuàng)建的判斷過程。@Transactional的作用一個就是標識方法需要被代理,一個就是攜帶事務(wù)管理需要的一些屬性信息。
動態(tài)代理邏輯實現(xiàn)
【aop實現(xiàn)原理分析】中知道,aop最終的代理對象的代理方法是
DynamicAdvisedInterceptor#intercept
所以我們可以在這個方法斷點分析代理邏輯。
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
//follow
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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 = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
通過分析
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
返回的是TransactionInterceptor,利用TransactionInterceptor是如何實現(xiàn)代理邏輯調(diào)用的?跟蹤
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
發(fā)現(xiàn)最終是調(diào)用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,從上面可以看到CglibMethodInvocation是包裝了目標對象的方法調(diào)用的所有必須信息,因此,在TransactionInterceptor#invoke里面也是可以調(diào)用目標方法的,并且還可以實現(xiàn)類似@Around的邏輯,在目標方法調(diào)用前后繼續(xù)注入一些其他邏輯,比如事務(wù)管理邏輯。
TransactionInterceptor–最終事務(wù)管理者
下面看代碼。
TransactionInterceptor#invoke
@Override
public Object invoke(final 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, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
繼續(xù)跟蹤invokeWithinTransaction,下面的代碼中其實就可以看出一些邏輯端倪,就是我們猜想的實現(xiàn)方式,事務(wù)管理。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//開啟事務(wù)
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.
//方法調(diào)用
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//回滾事務(wù)
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事務(wù)
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus 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.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
總結(jié)
最終可以總結(jié)一下整個流程,跟開始的猜想對照。

分析源碼后對照

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
微服務(wù)中使用Maven BOM來管理你的版本依賴詳解
這篇文章主要介紹了微服務(wù)中使用Maven BOM來管理你的版本依賴,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12
Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之二叉堆
二叉堆是一種特殊的堆,其實質(zhì)是完全二叉樹。二叉堆有兩種:最大堆和最小堆。最大堆是指父節(jié)點鍵值總是大于或等于任何一個子節(jié)點的鍵值。而最小堆恰恰相反,指的是父節(jié)點鍵值總是小于任何一個子節(jié)點的鍵值2022-02-02
java通過AES生成公鑰加密數(shù)據(jù)ECC加密公鑰
這篇文章主要為大家介紹了java通過AES生成公鑰加密數(shù)據(jù)ECC加密公鑰實現(xiàn)案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
SpringBoot整合Kaptcha實現(xiàn)圖形驗證碼功能
這篇文章主要介紹了SpringBoot整合Kaptcha實現(xiàn)圖形驗證碼功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09

