" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring源碼閱讀MethodInterceptor解析

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

概述

上一篇詳細(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ù)字小游戲

    java實(shí)現(xiàn)簡單的猜數(shù)字小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • 使用synchronized關(guān)鍵字實(shí)現(xiàn)信號量的方法

    使用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-04
  • IDEA+maven+SpringBoot+JPA+Thymeleaf實(shí)現(xiàn)Crud及分頁

    IDEA+maven+SpringBoot+JPA+Thymeleaf實(shí)現(xiàn)Crud及分頁

    這篇文章主要介紹了不需要電腦任何操作基于IDEA + maven + SpringBoot + JPA + Thymeleaf實(shí)現(xiàn)CRUD及分頁,需要的朋友可以參考下
    2018-03-03
  • 使用Spring動態(tài)修改bean屬性的key

    使用Spring動態(tài)修改bean屬性的key

    這篇文章主要介紹了使用Spring動態(tài)修改bean屬性的key方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用java實(shí)現(xiàn)http多線程斷點(diǎn)下載文件(二)

    使用java實(shí)現(xiàn)http多線程斷點(diǎn)下載文件(二)

    下載工具我想沒有幾個人不會用的吧,前段時間比較無聊,花了點(diǎn)時間用java寫了個簡單的http多線程下載程序,我實(shí)現(xiàn)的這個http下載工具功能很簡單,就是一個多線程以及一個斷點(diǎn)恢復(fù),當(dāng)然下載是必不可少的,需要的朋友可以參考下
    2012-12-12
  • SpringCloud Nacos集群搭建過程詳解

    SpringCloud Nacos集群搭建過程詳解

    Nacos集群不僅僅是服務(wù)注冊中心,還在微服務(wù)架構(gòu)中發(fā)揮著關(guān)鍵的角色,支持多種場景下的服務(wù)治理和協(xié)調(diào),本文介紹了如何在SpringCloud環(huán)境中搭建Nacos集群,為讀者提供了一份清晰而詳盡的指南,通過逐步演示每個關(guān)鍵步驟,讀者能夠輕松理解并操作整個搭建過程
    2024-02-02
  • 詳解SpringBoot實(shí)現(xiàn)fastdfs防盜鏈功能的示例代碼

    詳解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-10
  • HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究

    HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究

    這篇文章主要為大家介紹了HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 一篇文章教會你使用java爬取想要的資源

    一篇文章教會你使用java爬取想要的資源

    這篇文章主要介紹了使用java爬蟲爬取想要的資源,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • java并發(fā)編程之進(jìn)程和線程調(diào)度基礎(chǔ)詳解

    java并發(fā)編程之進(jìn)程和線程調(diào)度基礎(chǔ)詳解

    這篇文章主要介紹了java并發(fā)編程之進(jìn)程和線程調(diào)度基礎(chǔ),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論