spring源碼閱讀--@Transactional實(shí)現(xiàn)原理講解
@Transactional注解簡(jiǎn)介
@Transactional是spring中聲明式事務(wù)管理的注解配置方式,相信這個(gè)注解的作用大家都很清楚。
@Transactional注解可以幫助我們把事務(wù)開(kāi)啟、提交或者回滾的操作,通過(guò)aop的方式進(jìn)行管理。
通過(guò)@Transactional注解就能讓spring為我們管理事務(wù),免去了重復(fù)的事務(wù)管理邏輯,減少對(duì)業(yè)務(wù)代碼的侵入,使我們開(kāi)發(fā)人員能夠?qū)W⒂跇I(yè)務(wù)層面開(kāi)發(fā)。
我們知道實(shí)現(xiàn)@Transactional原理是基于spring aop,aop又是動(dòng)態(tài)代理模式的實(shí)現(xiàn),通過(guò)對(duì)源碼的閱讀,總結(jié)出下面的步驟來(lái)了解實(shí)際中,在spring 是如何利用aop來(lái)實(shí)現(xiàn)@Transactional的功能的。如果對(duì)spring的aop實(shí)現(xiàn)原理不了解,可以看aop實(shí)現(xiàn)原理分析。
spring中聲明式事務(wù)實(shí)現(xiàn)原理猜想
- 首先,對(duì)于spring中aop實(shí)現(xiàn)原理有了解的話,應(yīng)該知道想要對(duì)一個(gè)方法進(jìn)行代理的話,肯定需要定義切點(diǎn)。在@Transactional的實(shí)現(xiàn)中,同樣如此,spring為我們定義了以 @Transactional 注解為植入點(diǎn)的切點(diǎn),這樣才能知道@Transactional注解標(biāo)注的方法需要被代理。
- 有了切面定義之后,在spring的bean的初始化過(guò)程中,就需要對(duì)實(shí)例化的bean進(jìn)行代理,并且生成代理對(duì)象。
- 生成代理對(duì)象的代理邏輯中,進(jìn)行方法調(diào)用時(shí),需要先獲取切面邏輯,@Transactional注解的切面邏輯類似于@Around,在spring中是實(shí)現(xiàn)一種類似代理邏輯。
@Transactional作用
根據(jù)上面的原理猜想,下面簡(jiǎn)單介紹每個(gè)步驟的源碼以進(jìn)行驗(yàn)證。
首先是@Transactional,作用是定義代理植入點(diǎn)?!綼op實(shí)現(xiàn)原理分析】中,分析知道代理對(duì)象創(chuàng)建的通過(guò)BeanPostProcessor的實(shí)現(xiàn)類AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInstantiation方法來(lái)實(shí)現(xiàn)個(gè),如果需要進(jìn)行代理,那么在這個(gè)方法就會(huì)返回一個(gè)代理對(duì)象給容器,同時(shí)判斷植入點(diǎn)也是在這個(gè)方法中。
那么下面開(kāi)始分析,在配置好注解驅(qū)動(dòng)方式的事務(wù)管理之后,spring會(huì)在ioc容器創(chuàng)建一個(gè)BeanFactoryTransactionAttributeSourceAdvisor實(shí)例,這個(gè)實(shí)例可以看作是一個(gè)切點(diǎn),在判斷一個(gè)bean在初始化過(guò)程中是否需要?jiǎng)?chuàng)建代理對(duì)象,都需要驗(yàn)證一次BeanFactoryTransactionAttributeSourceAdvisor是否是適用這個(gè)bean的切點(diǎn)。如果是,就需要?jiǎng)?chuàng)建代理對(duì)象,并且把BeanFactoryTransactionAttributeSourceAdvisor實(shí)例注入到代理對(duì)象中。
其中【aop實(shí)現(xiàn)原理分析】知道在AopUtils#findAdvisorsThatCanApply中判斷切面是否適用當(dāng)前bean,可以在這個(gè)地方斷點(diǎn)分析調(diào)用堆棧,AopUtils#findAdvisorsThatCanApply一致調(diào)用,最終通過(guò)以下代碼判斷是否適用切點(diǎn)。
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass)
這里可以根據(jù)參數(shù)打上條件斷點(diǎn)進(jìn)行調(diào)試分析調(diào)用棧,targetClass就是目標(biāo)class
…一系列調(diào)用 最終
SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { //這里就是分析Method是否被@Transactional注解標(biāo)注,有的話,不用說(shuō)BeanFactoryTransactionAttributeSourceAdvisor適配當(dāng)前bean,進(jìn)行代理,并且注入切點(diǎn) //BeanFactoryTransactionAttributeSourceAdvisor AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class); if (attributes != null) { return parseTransactionAnnotation(attributes); } else { return null; } }
上面就是判斷是否需要根據(jù)@Transactional進(jìn)行代理對(duì)象創(chuàng)建的判斷過(guò)程。@Transactional的作用一個(gè)就是標(biāo)識(shí)方法需要被代理,一個(gè)就是攜帶事務(wù)管理需要的一些屬性信息。
動(dòng)態(tài)代理邏輯實(shí)現(xiàn)
【aop實(shí)現(xiàn)原理分析】中知道,aop最終的代理對(duì)象的代理方法是
DynamicAdvisedInterceptor#intercept
所以我們可以在這個(gè)方法斷點(diǎn)分析代理邏輯。
@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); } } }
通過(guò)分析
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
返回的是TransactionInterceptor,利用TransactionInterceptor是如何實(shí)現(xiàn)代理邏輯調(diào)用的?跟蹤
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
發(fā)現(xiàn)最終是調(diào)用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,從上面可以看到CglibMethodInvocation是包裝了目標(biāo)對(duì)象的方法調(diào)用的所有必須信息,因此,在TransactionInterceptor#invoke里面也是可以調(diào)用目標(biāo)方法的,并且還可以實(shí)現(xiàn)類似@Around的邏輯,在目標(biāo)方法調(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,下面的代碼中其實(shí)就可以看出一些邏輯端倪,就是我們猜想的實(shí)現(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. //開(kāi)啟事務(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é)一下整個(gè)流程,跟開(kāi)始的猜想對(duì)照。
分析源碼后對(duì)照
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
微服務(wù)中使用Maven BOM來(lái)管理你的版本依賴詳解
這篇文章主要介紹了微服務(wù)中使用Maven BOM來(lái)管理你的版本依賴,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12SpringBoot整合Mysql和Redis的詳細(xì)過(guò)程
這篇文章主要介紹了SpringBoot整合Mysql和Redis的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02springboot實(shí)現(xiàn)訪問(wèn)多個(gè)redis庫(kù)
這篇文章主要介紹了springboot實(shí)現(xiàn)訪問(wèn)多個(gè)redis庫(kù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot項(xiàng)目熱部署的實(shí)現(xiàn)
SpringBoot項(xiàng)目熱部署是一種讓開(kāi)發(fā)人員在修改代碼后無(wú)需重啟應(yīng)用即可看到更改效果的技術(shù),通過(guò)使用SpringBoot的DevTools等工具,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之二叉堆
二叉堆是一種特殊的堆,其實(shí)質(zhì)是完全二叉樹(shù)。二叉堆有兩種:最大堆和最小堆。最大堆是指父節(jié)點(diǎn)鍵值總是大于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值。而最小堆恰恰相反,指的是父節(jié)點(diǎn)鍵值總是小于任何一個(gè)子節(jié)點(diǎn)的鍵值2022-02-02java通過(guò)AES生成公鑰加密數(shù)據(jù)ECC加密公鑰
這篇文章主要為大家介紹了java通過(guò)AES生成公鑰加密數(shù)據(jù)ECC加密公鑰實(shí)現(xiàn)案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12MyBatis之關(guān)于動(dòng)態(tài)SQL解讀
這篇文章主要介紹了MyBatis之關(guān)于動(dòng)態(tài)SQL解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06SpringBoot整合Kaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能
這篇文章主要介紹了SpringBoot整合Kaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09