" />

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

Spring源碼閱讀MethodInterceptor解析

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

概述

上一篇詳細(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)類(lèi)型,攔截器的具體類(lèi)型也有 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)類(lèi)型的攔截器的具體類(lèi)型,它們分別是:

  • 實(shí)現(xiàn)了 MethodInterceptor 接口的三個(gè) Advice 實(shí)現(xiàn)類(lèi) AspectJAroundAdvice / AspectJAfterThrowingAdvice / AspectJAfterAdvice
  • 兩個(gè)通過(guò)適配器封裝了 Advice 對(duì)象的攔截器類(lèi)型 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)類(lèi),根據(jù)前面的邏輯,我們知道這里傳入?yún)?shù)的對(duì)象是之前創(chuàng)建的 ReflectiveMethodInvocation 對(duì)象,前面創(chuàng)建 ReflectiveMethodInvocation 對(duì)象的時(shí)候,我們?cè)?jīng)分析過(guò)它的類(lèi)關(guān)系,它是 ProxyMethodInvocation 接口的實(shí)現(xiàn)類(lèi)。因此,這個(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)在父類(lèi) 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)方法指定的異?;蛘咂渥宇?lèi)的時(shí)候,才會(huì)執(zhí)行異常拋出后的增強(qiáng)邏輯,否則,不執(zhí)行增強(qiáng)邏輯,直接將異常拋出。

那如何在增強(qiáng)方法上指定異常的類(lèi)型呢,我們還是看一個(gè)例子:

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

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

回到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 類(lèi)型的增強(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)類(lèi),因此,其對(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)方法的返回值類(lèi)型跟增強(qiáng)方法上面聲明的返回值類(lèi)型是否匹配,只有匹配的情況下,才會(huì)執(zhí)行增強(qiáng)邏輯。

在增強(qiáng)方法上聲明返回值類(lèi)型的方法參考下面的例子:

@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)類(lèi)型對(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ù)字小游戲

    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)量的方法

    使用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è)

    這篇文章主要介紹了不需要電腦任何操作基于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

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

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

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

    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防盜鏈功能的示例代碼

    詳解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解析另一種選擇深入研究

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

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

    這篇文章主要介紹了使用java爬蟲(chóng)爬取想要的資源,本文給大家介紹的非常詳細(xì),對(duì)大家的學(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à)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評(píng)論