Spring源碼閱讀MethodInterceptor解析
概述
上一篇詳細(xì)分析了 ReflectiveMethodInvocation 中的proceed
方法的邏輯,它通過遞歸的方式,完成攔截器鏈中增強(qiáng)邏輯的逐層執(zhí)行。當(dāng)方法中每次根據(jù)索引獲取到的當(dāng)前攔截器需要執(zhí)行增強(qiáng)邏輯的時候,由于proceed
方法并沒有直接在方法體內(nèi)調(diào)用自身,而是調(diào)用了攔截器的invoke
方法,之后攔截器的invoke
方法又調(diào)用了proceed
方法,因此,每一次遞歸都是通過proceed
方法和攔截器的invoke
方法共同完成的。
所以,這一篇來分析攔截器的invoke
方法,針對 5 種增強(qiáng)類型,攔截器的具體類型也有 5 種,它們的invoke
方法實(shí)現(xiàn)各不相同,本文會逐個分析。
MethodInterceptor 分析
首先,這 5 種攔截器都是 MethodInterceptor 接口的實(shí)現(xiàn),而invoke
方法正是在 MethodInterceptor 接口中定義的。
@FunctionalInterface public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; }
在invoke
方法被調(diào)用的時候,傳入的invocation
參數(shù)就是之前創(chuàng)建的 ReflectiveMethodInvocation 對象,在invoke
方法的具體實(shí)現(xiàn)中,正是通過它調(diào)用proceed
方法完成了遞歸。
在之前的文章中,分析過 JdkDynamicAopProxy 的invoke
方法獲取攔截器鏈的步驟,也從中了解了 5 中增強(qiáng)類型的攔截器的具體類型,它們分別是:
- 實(shí)現(xiàn)了 MethodInterceptor 接口的三個 Advice 實(shí)現(xiàn)類 AspectJAroundAdvice / AspectJAfterThrowingAdvice / AspectJAfterAdvice
- 兩個通過適配器封裝了 Advice 對象的攔截器類型 MethodBeforeAdviceInterceptor / AfterReturningAdviceInterceptor。
下面進(jìn)入分析的環(huán)節(jié),分析的主要內(nèi)容是他們的invoke
方法。
AspectJAroundAdvice 分析
// org.springframework.aop.aspectj.AspectJAroundAdvice#invoke @Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); }
方法中,首先會確保參數(shù)傳入的mi是否是是 ProxyMethodInvocation 的實(shí)現(xiàn)類,根據(jù)前面的邏輯,我們知道這里傳入?yún)?shù)的對象是之前創(chuàng)建的 ReflectiveMethodInvocation 對象,前面創(chuàng)建 ReflectiveMethodInvocation 對象的時候,我們曾經(jīng)分析過它的類關(guān)系,它是 ProxyMethodInvocation 接口的實(shí)現(xiàn)類。因此,這個方法中后續(xù)的流程會正常執(zhí)行。
接下來,通過lazyGetProceedingJoinPoint
方法得到了一個 ProceedingJoinPoint 對象pjp
,我們看一下這個方法中作了什么。
// org.springframework.aop.aspectj.AspectJAroundAdvice#lazyGetProceedingJoinPoint protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) { return new MethodInvocationProceedingJoinPoint(rmi); }
這里的邏輯,就是將invoke方法傳入的 MethodInvocation 參數(shù),通過構(gòu)造方法,封裝成了一個 MethodInvocationProceedingJoinPoint 對象,它的構(gòu)造方法中除了把參數(shù)賦值給成員變量,并無更多的邏輯。
public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) { Assert.notNull(methodInvocation, "MethodInvocation must not be null"); this.methodInvocation = methodInvocation; }
回到invoke方法中,下一步是通過getJoinPointMatch
方法獲取到一個 JoinPointMatch 對象,它會在之后綁定增強(qiáng)方法的參數(shù)值時用到。
方法的最后,就是執(zhí)行invokeAdviceMethod
方法,從它的名字可以看出,這里就是執(zhí)行增強(qiáng)方法的邏輯,我們進(jìn)入這個方法查看。
// org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethod(org.aspectj.lang.JoinPoint, org.aspectj.weaver.tools.JoinPointMatch, java.lang.Object, java.lang.Throwable) protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); }
這個方法的實(shí)現(xiàn)在父類 AbstractAspectJAdvice 中,方法只有一句,就是執(zhí)行invokeAdviceMethodWithGivenArgs
方法并返回結(jié)果,在方法的參數(shù)中,argBinding
用于根據(jù)增強(qiáng)方法的參數(shù)列表,來綁定執(zhí)行增強(qiáng)方法的參數(shù)值。我們進(jìn)入invokeAdviceMethodWithGivenArgs
方法查看。
// org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); // TODO AopUtils.invokeJoinpointUsingReflection return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
這里的邏輯就比較簡單了,就是獲取到當(dāng)前攔截器中的增強(qiáng)方法,并通過反射執(zhí)行這個方法。
我們都知道,AspectJAroundAdvice 是環(huán)繞增強(qiáng),也就是在目標(biāo)方法執(zhí)行的前后都有增強(qiáng)邏輯,那么,目標(biāo)方法(或者下一層攔截器)是什么時候調(diào)用的呢?可以通過一個例子來分析。
以下是一個典型的環(huán)繞增強(qiáng)的方法:
@Around("point()") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("Around Before ..."); // 被增強(qiáng)的方法 proceedingJoinPoint.proceed(); System.out.println("Around After ..."); }
在一個環(huán)繞增強(qiáng)的方法中,會通過方法參數(shù)中,代表當(dāng)前連接點(diǎn)的 ProceedingJoinPoint 對象的proceed()
來執(zhí)行目標(biāo)方法(或者下一層攔截器),其實(shí)就是調(diào)用了我們上一篇中分析的 ReflectiveMethodInvocation 的proceed
方法。而這個方法被調(diào)用時,參數(shù)中的 ProceedingJoinPoint 的值,是在前面的步驟中,argBinding
方法中進(jìn)行綁定的。
以上就是 AspectJAroundAdvice 中,增強(qiáng)邏輯的執(zhí)行過程。
AspectJAfterThrowingAdvice 分析
// org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
在 AspectJAfterThrowingAdvice 中,invoke
方法相對更加簡單。AspectJAfterThrowingAdvice 是在方法拋出異常后,執(zhí)行增強(qiáng)邏輯,在invoke
方法中,也能很簡單地對應(yīng)到這樣的邏輯。
首先,進(jìn)入方法后會直接執(zhí)行mi.proceed()
,也就是執(zhí)行 ReflectiveMethodInvocation 的proceed
方法的下一層遞歸,如果后續(xù)的執(zhí)行拋出了異常,則執(zhí)行catch
語句塊中的內(nèi)容,其中就包含了增強(qiáng)邏輯的執(zhí)行。
不過,在執(zhí)行增強(qiáng)邏輯之前,還通過shouldInvokeOnThrowing
方法進(jìn)行了一次判斷,我們查看方判斷的邏輯。
// org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#shouldInvokeOnThrowing private boolean shouldInvokeOnThrowing(Throwable ex) { return getDiscoveredThrowingType().isAssignableFrom(ex.getClass()); }
這里的邏輯比較簡單,就是只有拋出的異常是增強(qiáng)方法指定的異?;蛘咂渥宇惖臅r候,才會執(zhí)行異常拋出后的增強(qiáng)邏輯,否則,不執(zhí)行增強(qiáng)邏輯,直接將異常拋出。
那如何在增強(qiáng)方法上指定異常的類型呢,我們還是看一個例子:
@AfterThrowing(value = "point()", throwing = "ex") public void afterThrowing(JoinPoint joinPoint, Exception ex) { System.out.println("AfterThrowing..."); }
在以上的例子中,@AfterThrowing
注解指定了throwing
屬性為ex
,那么增強(qiáng)方法中,ex
參數(shù)的類型 Exception 就是指定的異常類型。
回到invoke
方法中,執(zhí)行增強(qiáng)邏輯的方式是調(diào)用invokeAdviceMethod
方法,與環(huán)繞增強(qiáng)的增強(qiáng)方法執(zhí)行時調(diào)用的invokeAdviceMethod
相比,這里少一個參數(shù),也就是 JoinPoint 參數(shù),這是因?yàn)?AspectJAfterThrowingAdvice 類型的增強(qiáng)方法并不需要在增強(qiáng)邏輯中處理目標(biāo)方法的調(diào)用,我們進(jìn)入這個重載方法查看代碼。
protected Object invokeAdviceMethod( @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); }
發(fā)現(xiàn),邏輯上并沒有什么區(qū)別,就只是少綁定一個參數(shù)而已。
另外,這里還有一點(diǎn)需要留意,在調(diào)用invokeAdviceMethod
方法時,會將拋出的異常對象作為參數(shù)傳入,這也很好理解 ,因?yàn)樵鰪?qiáng)方法中可以包含異常對象的參數(shù)。
以上是 AspectJAfterThrowingAdvice 中,增強(qiáng)邏輯的執(zhí)行過程。
AspectJAfterAdvice 分析
// org.springframework.aop.aspectj.AspectJAfterAdvice#invoke @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); } }
在 AspectJAfterAdvice 中,invoke
方法的邏輯更加簡單。我們知道,后置增強(qiáng)會在目標(biāo)方法執(zhí)行之后再執(zhí)行,而且,無論是否拋出了一場,都會執(zhí)行。因此,在invoke
方法中,會先執(zhí)行 MethodInvocation 的proceed
方法,然后,在finally
語句塊中,執(zhí)行增強(qiáng)方法。
增強(qiáng)方法的執(zhí)行,仍然是通過invokeAdviceMethod
方法,這里就不再介紹了。
以上是 AspectJAfterAdvice 中,增強(qiáng)邏輯的執(zhí)行過程。
MethodBeforeAdviceInterceptor 分析
// org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor#invoke @Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
前置增強(qiáng) MethodBeforeAdvice 與上述的三種增強(qiáng)不同,它并不是 MethodInterceptor 的實(shí)現(xiàn)類,因此,其對應(yīng)的攔截器是封裝了 MethodBeforeAdvice 的 MethodBeforeAdviceInterceptor。
因?yàn)榍爸迷鰪?qiáng)是在目標(biāo)方法執(zhí)行前執(zhí)行,既然在執(zhí)行前,則不會涉及到目標(biāo)方法的異常和返回值,可以說,前置增強(qiáng)的邏輯和目標(biāo)方法的邏輯,相對比較獨(dú)立。因此,在上面的invoke方法中,分別執(zhí)行兩者即可。
這里我們看一下前置增強(qiáng)方法邏輯的執(zhí)行。首先,advice
成員變量就是在 MethodBeforeAdviceInterceptor 中封裝的 MethodBeforeAdvice 對象,我們找到它的before
方法,具體實(shí)現(xiàn)邏輯在 AspectJMethodBeforeAdvice 中。
// org.springframework.aop.aspectj.AspectJMethodBeforeAdvice#before @Override public void before(Method method, Object[] args, @Nullable Object target) throws Throwable { invokeAdviceMethod(getJoinPointMatch(), null, null); }
可以看到,這里依然是通過invokeAdviceMethod
方法來執(zhí)行增強(qiáng)邏輯的,與之前的幾種增強(qiáng)方法對應(yīng)的執(zhí)行邏輯相同。
以上是 MethodBeforeAdviceInterceptor 中,增強(qiáng)邏輯的執(zhí)行過程。
AfterReturningAdviceInterceptor 分析
// org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke @Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
在 AfterReturningAdviceInterceptor 的invoke
方法中,會先執(zhí)行 MethodInvocation 的proceed
方法,并獲取返回值,然后,在上一步執(zhí)行沒有發(fā)生異常的情況下,執(zhí)行增強(qiáng)方法,最后將proceed
的返回值返回。
增強(qiáng)方法通過調(diào)用advice
成員變量的afterReturning
來完成,這里有一點(diǎn)要留意的是,增強(qiáng)邏輯的執(zhí)行,會將上一步proceed
得返回值作為參數(shù)傳入。我們進(jìn)入afterReturning
方法。
// org.springframework.aop.aspectj.AspectJAfterReturningAdvice#afterReturning @Override public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable { if (shouldInvokeOnReturnValueOf(method, returnValue)) { invokeAdviceMethod(getJoinPointMatch(), returnValue, null); } }
具體的實(shí)現(xiàn)邏輯在 AspectJAfterReturningAdvice 中,在執(zhí)行具體的增強(qiáng)邏輯之前,還會進(jìn)行一次判斷,這里的邏輯跟 AspectJAfterThrowingAdvice 中判斷異常的邏輯非常相似,shouldInvokeOnReturnValueOf
方法會判斷目標(biāo)方法的返回值類型跟增強(qiáng)方法上面聲明的返回值類型是否匹配,只有匹配的情況下,才會執(zhí)行增強(qiáng)邏輯。
在增強(qiáng)方法上聲明返回值類型的方法參考下面的例子:
@AfterReturning(value = "point()", returning = "o") public void afterReturning(Object o) { System.out.println("AfterReturning..."); }
增強(qiáng)方法依然是通過 invokeAdviceMethod 執(zhí)行的,只不過這里要講上一步的返回值傳入,以便增強(qiáng)方法能夠通過參數(shù)綁定獲得到這個值。
以上是 AfterReturningAdviceInterceptor 中,增強(qiáng)邏輯的執(zhí)行過程。
總結(jié)
本文詳細(xì)分析了 Spring AOP 中五種增強(qiáng)類型對應(yīng)的攔截器中增強(qiáng)方法的執(zhí)行邏輯,結(jié)合上一篇中分析的 ReflectiveMethodInvocation 中proceed
方法的執(zhí)行邏輯,就組成了完整的攔截器鏈遞歸調(diào)用的邏輯。
以上就是Spring源碼閱讀MethodInterceptor解析的詳細(xì)內(nèi)容,更多關(guān)于Spring MethodInterceptor的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實(shí)現(xiàn)簡單的猜數(shù)字小游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03使用synchronized關(guān)鍵字實(shí)現(xiàn)信號量的方法
在Java中,信號量(Semaphore)是一種常用的同步工具,它可以用來控制對共享資源的訪問數(shù)量,下面,我們將使用Synchronized關(guān)鍵字來實(shí)現(xiàn)一個簡單的信號量,我們的目標(biāo)是實(shí)現(xiàn)一個計(jì)數(shù)信號量,其中信號量的計(jì)數(shù)指示可以同時訪問某一資源的線程數(shù),需要的朋友可以參考下2024-04-04IDEA+maven+SpringBoot+JPA+Thymeleaf實(shí)現(xiàn)Crud及分頁
這篇文章主要介紹了不需要電腦任何操作基于IDEA + maven + SpringBoot + JPA + Thymeleaf實(shí)現(xiàn)CRUD及分頁,需要的朋友可以參考下2018-03-03使用java實(shí)現(xiàn)http多線程斷點(diǎn)下載文件(二)
下載工具我想沒有幾個人不會用的吧,前段時間比較無聊,花了點(diǎn)時間用java寫了個簡單的http多線程下載程序,我實(shí)現(xiàn)的這個http下載工具功能很簡單,就是一個多線程以及一個斷點(diǎn)恢復(fù),當(dāng)然下載是必不可少的,需要的朋友可以參考下2012-12-12詳解SpringBoot實(shí)現(xiàn)fastdfs防盜鏈功能的示例代碼
我們可以通過fastdfs實(shí)現(xiàn)一個分布式文件系統(tǒng),如果fastdfs部署在外網(wǎng),那么任何一個人知道了上傳接口,就可以實(shí)現(xiàn)文件的上傳和訪問。那么如何阻止他人訪問我們fastdfs服務(wù)器上的文件呢?此處就需要使用fastdfs的防盜鏈功能,本文就來講講如何實(shí)現(xiàn)這一功能2022-10-10HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究
這篇文章主要為大家介紹了HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10java并發(fā)編程之進(jìn)程和線程調(diào)度基礎(chǔ)詳解
這篇文章主要介紹了java并發(fā)編程之進(jìn)程和線程調(diào)度基礎(chǔ),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06