" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring源碼閱讀MethodInterceptor解析

 更新時間:2022年11月02日 09:40:53   作者:Pseudocode  
這篇文章主要為大家介紹了Spring源碼閱讀MethodInterceptor使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

概述

上一篇詳細分析了 ReflectiveMethodInvocation 中的proceed方法的邏輯,它通過遞歸的方式,完成攔截器鏈中增強邏輯的逐層執(zhí)行。當(dāng)方法中每次根據(jù)索引獲取到的當(dāng)前攔截器需要執(zhí)行增強邏輯的時候,由于proceed方法并沒有直接在方法體內(nèi)調(diào)用自身,而是調(diào)用了攔截器的invoke方法,之后攔截器的invoke方法又調(diào)用了proceed方法,因此,每一次遞歸都是通過proceed方法和攔截器的invoke方法共同完成的。

所以,這一篇來分析攔截器的invoke方法,針對 5 種增強類型,攔截器的具體類型也有 5 種,它們的invoke方法實現(xiàn)各不相同,本文會逐個分析。

MethodInterceptor 分析

首先,這 5 種攔截器都是 MethodInterceptor 接口的實現(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方法的具體實現(xiàn)中,正是通過它調(diào)用proceed方法完成了遞歸。

在之前的文章中,分析過 JdkDynamicAopProxy 的invoke方法獲取攔截器鏈的步驟,也從中了解了 5 中增強類型的攔截器的具體類型,它們分別是:

  • 實現(xiàn)了 MethodInterceptor 接口的三個 Advice 實現(xiàn)類 AspectJAroundAdvice / AspectJAfterThrowingAdvice / AspectJAfterAdvice
  • 兩個通過適配器封裝了 Advice 對象的攔截器類型 MethodBeforeAdviceInterceptor / AfterReturningAdviceInterceptor。

下面進入分析的環(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 的實現(xiàn)類,根據(jù)前面的邏輯,我們知道這里傳入?yún)?shù)的對象是之前創(chuàng)建的 ReflectiveMethodInvocation 對象,前面創(chuàng)建 ReflectiveMethodInvocation 對象的時候,我們曾經(jīng)分析過它的類關(guān)系,它是 ProxyMethodInvocation 接口的實現(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 對象,它會在之后綁定增強方法的參數(shù)值時用到。

方法的最后,就是執(zhí)行invokeAdviceMethod方法,從它的名字可以看出,這里就是執(zhí)行增強方法的邏輯,我們進入這個方法查看。

// 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));
}

這個方法的實現(xiàn)在父類 AbstractAspectJAdvice 中,方法只有一句,就是執(zhí)行invokeAdviceMethodWithGivenArgs方法并返回結(jié)果,在方法的參數(shù)中,argBinding用于根據(jù)增強方法的參數(shù)列表,來綁定執(zhí)行增強方法的參數(shù)值。我們進入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)前攔截器中的增強方法,并通過反射執(zhí)行這個方法。

我們都知道,AspectJAroundAdvice 是環(huán)繞增強,也就是在目標(biāo)方法執(zhí)行的前后都有增強邏輯,那么,目標(biāo)方法(或者下一層攔截器)是什么時候調(diào)用的呢?可以通過一個例子來分析。

以下是一個典型的環(huán)繞增強的方法:

@Around("point()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("Around Before ...");
    // 被增強的方法
    proceedingJoinPoint.proceed();
    System.out.println("Around After ...");
}

在一個環(huán)繞增強的方法中,會通過方法參數(shù)中,代表當(dāng)前連接點的 ProceedingJoinPoint 對象的proceed()來執(zhí)行目標(biāo)方法(或者下一層攔截器),其實就是調(diào)用了我們上一篇中分析的 ReflectiveMethodInvocation 的proceed方法。而這個方法被調(diào)用時,參數(shù)中的 ProceedingJoinPoint 的值,是在前面的步驟中,argBinding方法中進行綁定的。

以上就是 AspectJAroundAdvice 中,增強邏輯的執(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í)行增強邏輯,在invoke方法中,也能很簡單地對應(yīng)到這樣的邏輯。

首先,進入方法后會直接執(zhí)行mi.proceed(),也就是執(zhí)行 ReflectiveMethodInvocation 的proceed方法的下一層遞歸,如果后續(xù)的執(zhí)行拋出了異常,則執(zhí)行catch語句塊中的內(nèi)容,其中就包含了增強邏輯的執(zhí)行。

不過,在執(zhí)行增強邏輯之前,還通過shouldInvokeOnThrowing方法進行了一次判斷,我們查看方判斷的邏輯。

// org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#shouldInvokeOnThrowing
private boolean shouldInvokeOnThrowing(Throwable ex) {
   return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
}

這里的邏輯比較簡單,就是只有拋出的異常是增強方法指定的異?;蛘咂渥宇惖臅r候,才會執(zhí)行異常拋出后的增強邏輯,否則,不執(zhí)行增強邏輯,直接將異常拋出。

那如何在增強方法上指定異常的類型呢,我們還是看一個例子:

@AfterThrowing(value = "point()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
    System.out.println("AfterThrowing...");
}

在以上的例子中,@AfterThrowing注解指定了throwing屬性為ex,那么增強方法中,ex參數(shù)的類型 Exception 就是指定的異常類型。

回到invoke方法中,執(zhí)行增強邏輯的方式是調(diào)用invokeAdviceMethod方法,與環(huán)繞增強的增強方法執(zhí)行時調(diào)用的invokeAdviceMethod相比,這里少一個參數(shù),也就是 JoinPoint 參數(shù),這是因為 AspectJAfterThrowingAdvice 類型的增強方法并不需要在增強邏輯中處理目標(biāo)方法的調(diào)用,我們進入這個重載方法查看代碼。

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ào)用invokeAdviceMethod方法時,會將拋出的異常對象作為參數(shù)傳入,這也很好理解 ,因為增強方法中可以包含異常對象的參數(shù)。

以上是 AspectJAfterThrowingAdvice 中,增強邏輯的執(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方法的邏輯更加簡單。我們知道,后置增強會在目標(biāo)方法執(zhí)行之后再執(zhí)行,而且,無論是否拋出了一場,都會執(zhí)行。因此,在invoke方法中,會先執(zhí)行 MethodInvocation 的proceed方法,然后,在finally語句塊中,執(zhí)行增強方法。

增強方法的執(zhí)行,仍然是通過invokeAdviceMethod方法,這里就不再介紹了。

以上是 AspectJAfterAdvice 中,增強邏輯的執(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();
}

前置增強 MethodBeforeAdvice 與上述的三種增強不同,它并不是 MethodInterceptor 的實現(xiàn)類,因此,其對應(yīng)的攔截器是封裝了 MethodBeforeAdvice 的 MethodBeforeAdviceInterceptor。

因為前置增強是在目標(biāo)方法執(zhí)行前執(zhí)行,既然在執(zhí)行前,則不會涉及到目標(biāo)方法的異常和返回值,可以說,前置增強的邏輯和目標(biāo)方法的邏輯,相對比較獨立。因此,在上面的invoke方法中,分別執(zhí)行兩者即可。

這里我們看一下前置增強方法邏輯的執(zhí)行。首先,advice成員變量就是在 MethodBeforeAdviceInterceptor 中封裝的 MethodBeforeAdvice 對象,我們找到它的before方法,具體實現(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í)行增強邏輯的,與之前的幾種增強方法對應(yīng)的執(zhí)行邏輯相同。

以上是 MethodBeforeAdviceInterceptor 中,增強邏輯的執(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í)行增強方法,最后將proceed的返回值返回。

增強方法通過調(diào)用advice成員變量的afterReturning來完成,這里有一點要留意的是,增強邏輯的執(zhí)行,會將上一步proceed得返回值作為參數(shù)傳入。我們進入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);
   }
}

具體的實現(xiàn)邏輯在 AspectJAfterReturningAdvice 中,在執(zhí)行具體的增強邏輯之前,還會進行一次判斷,這里的邏輯跟 AspectJAfterThrowingAdvice 中判斷異常的邏輯非常相似,shouldInvokeOnReturnValueOf方法會判斷目標(biāo)方法的返回值類型跟增強方法上面聲明的返回值類型是否匹配,只有匹配的情況下,才會執(zhí)行增強邏輯。

在增強方法上聲明返回值類型的方法參考下面的例子:

@AfterReturning(value = "point()", returning = "o")
public void afterReturning(Object o) {
    System.out.println("AfterReturning...");
}

增強方法依然是通過 invokeAdviceMethod 執(zhí)行的,只不過這里要講上一步的返回值傳入,以便增強方法能夠通過參數(shù)綁定獲得到這個值。

以上是 AfterReturningAdviceInterceptor 中,增強邏輯的執(zhí)行過程。

總結(jié)

本文詳細分析了 Spring AOP 中五種增強類型對應(yīng)的攔截器中增強方法的執(zhí)行邏輯,結(jié)合上一篇中分析的 ReflectiveMethodInvocation 中proceed方法的執(zhí)行邏輯,就組成了完整的攔截器鏈遞歸調(diào)用的邏輯。

以上就是Spring源碼閱讀MethodInterceptor解析的詳細內(nèi)容,更多關(guān)于Spring MethodInterceptor的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • idea SpringBoot+Gradle環(huán)境配置到項目打包

    idea SpringBoot+Gradle環(huán)境配置到項目打包

    Gradle是一個基于Java應(yīng)用的項目自動化構(gòu)建工具,本文介紹了在IDEA中創(chuàng)建Spring Boot Gradle項目,項目配置包括init.gradle和settings.gradle,感興趣的可以了解一下
    2024-11-11
  • Java使用MySQL實現(xiàn)連接池代碼實例

    Java使用MySQL實現(xiàn)連接池代碼實例

    這篇文章主要介紹了Java使用MySQL實現(xiàn)連接池代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Springcloud Eureka配置及集群代碼實例

    Springcloud Eureka配置及集群代碼實例

    這篇文章主要介紹了Springcloud Eureka配置及集群代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • Springboot3+Vue3實現(xiàn)JWT登錄鑒權(quán)功能

    Springboot3+Vue3實現(xiàn)JWT登錄鑒權(quán)功能

    JWT用于在網(wǎng)絡(luò)應(yīng)用間安全的傳遞消息,它以緊湊且自包含的方式,通過JSON對象在各方之間傳遞經(jīng)過驗證的信息,這篇文章主要介紹了Springboot3+Vue3實現(xiàn)JWT登錄鑒權(quán)功能,需要的朋友可以參考下
    2025-03-03
  • 詳解spring與shiro集成

    詳解spring與shiro集成

    這篇文章主要介紹了詳解spring與shiro集成,需要的朋友可以參考下
    2017-09-09
  • MyBatis-Plus Generator配置詳解

    MyBatis-Plus Generator配置詳解

    這篇文章主要介紹了MyBatis-Plus Generator配置詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java累加和校驗實現(xiàn)方式16進制(推薦)

    java累加和校驗實現(xiàn)方式16進制(推薦)

    下面小編就為大家?guī)硪黄猨ava累加和校驗實現(xiàn)方式16進制(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • IDEA中Maven依賴包下載不了的問題解決方案匯總

    IDEA中Maven依賴包下載不了的問題解決方案匯總

    這篇文章主要介紹了IDEA中Maven依賴包下載不了的問題解決方案匯總,文中通過圖文示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java反射實現(xiàn)javabean轉(zhuǎn)json實例代碼

    java反射實現(xiàn)javabean轉(zhuǎn)json實例代碼

    基于java反射機制實現(xiàn)javabean轉(zhuǎn)json字符串實例,大家參考使用吧
    2013-12-12
  • Java面試題篇之Sleep()方法與Wait()方法的區(qū)別詳解

    Java面試題篇之Sleep()方法與Wait()方法的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于Java面試題篇之Sleep()方法與Wait()方法區(qū)別的相關(guān)資料,wait()是Object類中的方法,而sleep()是Thread類中的靜態(tài)方法,wait()方法用于多個線程之間的協(xié)作和通信,而sleep()方法用于線程的休眠,需要的朋友可以參考下
    2024-07-07

最新評論

商城县| 湟源县| 汶川县| 万全县| 内黄县| 桦南县| 得荣县| 蓬溪县| 渝中区| 凭祥市| 洛浦县| 台山市| 本溪市| 尼勒克县| 灵武市| 株洲市| 滨州市| 天峻县| 炎陵县| 子洲县| 达拉特旗| 田林县| 延庆县| 昆山市| 南投县| 闽清县| 社旗县| 文昌市| 嘉黎县| 塔城市| 刚察县| 正安县| 保亭| 河曲县| 呼玛县| 乐清市| 绍兴县| 句容市| 崇州市| 名山县| 桂平市|