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

