springmvc比HandlerInterceptor更靠近目標(biāo)方法執(zhí)行的擴(kuò)展點(diǎn)WebMvcRegistrations

springmvc中常見的請(qǐng)求攔截器
  1. javax.servlet.Filter 的實(shí)現(xiàn)(默認(rèn)tomcat的實(shí)現(xiàn))
  2. org.springframework.web.servlet.HandlerInterceptor
    那么Filter是在更外層,也就是在請(qǐng)求目標(biāo)方法之前先執(zhí)行Filter的攔截器,再執(zhí)行HandlerInterceptor
    那么當(dāng)這兩個(gè)攔截器都不滿足某些場(chǎng)景,而這個(gè)場(chǎng)景又想要更靠近目標(biāo)方法執(zhí)行時(shí)機(jī)的時(shí)候,就想要找到一個(gè)更靠近請(qǐng)求目標(biāo)方法的擴(kuò)展點(diǎn),下述為尋找思路。

首先從HandlerInterceptor#preHandle入手

直接看這個(gè)方法由誰調(diào)用,如果沒有下載對(duì)應(yīng)源碼包,可以通過debug查看


直接找到堆棧
可以看到preHandle和postHandle的執(zhí)行
// 將要調(diào)用目標(biāo)方法
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

跟蹤堆棧后進(jìn)入AbstractHandlerMethodAdapter#handle ,并且當(dāng)前實(shí)例是RequestMappingHandlerAdapter


AbstractHandlerMethodAdapter#handle

繼續(xù)跟蹤


AbstractHandlerMethodAdapter#handleInternal

繼續(xù)跟蹤


RequestMappingHandlerAdapter#invokeHandlerMethod

然后我們可以看到執(zhí)行目標(biāo)方法 又委托給了ServletInvocableHandlerMethod

ServletInvocableHandlerMethod#invokeAndHandle

然后委托給invokeForRequest方法 再到doInvoke,走到這里我們已經(jīng)看到invokeForRequest中的邏輯,先通過
getMethodArgumentValues獲取到目標(biāo)方法的參數(shù),然后真正的執(zhí)行doInvoke方法帶上了參數(shù),可以預(yù)計(jì)到doInvoke方法已經(jīng)是非??拷鼒?zhí)行目標(biāo)方法了,所有準(zhǔn)備工作已經(jīng)完成,參數(shù)也構(gòu)造完了,并且所有方法public,protect也都沒有final,也就是這些方法都可以被重寫!
如果是重寫的話doInvoke是一個(gè)比較好的時(shí)機(jī),因?yàn)槟繕?biāo)方法的參數(shù)也構(gòu)造完畢。并且protect還不帶final真的太像是設(shè)計(jì)為被重寫的方法一樣。。。

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        return doInvoke(args);
    }

    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        Method method = getBridgedMethod();
        try {
            if (KotlinDetector.isSuspendingFunction(method)) {
                return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
            }
            return method.invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(method, getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }

如上述重寫 doInvoke那么我們需要能夠繼承到當(dāng)前類InvocableHandlerMethod
突然又想到了上面debug過程中看到了構(gòu)建InvocableHandlerMethod這一行代碼如下

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 如果經(jīng)常閱讀源碼,上面所描述的 doInvoke是protect非final并且這里又構(gòu)建了對(duì)應(yīng)實(shí)例,這就很明顯了寫代碼的人就是這樣設(shè)計(jì)的,繼續(xù)進(jìn)入這個(gè)方法看個(gè)究竟
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 省略代碼 ...

那么我們進(jìn)入到 createInvocableHnadlerMethod方法查看

// 看到這里,繼續(xù)是protect,非final,方法名,以及這簡(jiǎn)單粗暴的return一個(gè)new 實(shí)例,堅(jiān)定了前面的所有想法!
    protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
        return new ServletInvocableHandlerMethod(handlerMethod);
    }

到這里我們已經(jīng)非常確定以及肯定,doInvoke 以及ServletInvocableHandlerMethod 實(shí)例獲取方法createInvocableHandlerMethod就是被設(shè)計(jì)為讓我們可以擴(kuò)展的一系列操作

當(dāng)前實(shí)例為RequestMappingHandlerAdapter,那么我們就需要繼承它來重寫RequestMappingHandlerAdapter#createInvocableHandlerMethod就可以返回一個(gè)我們自己的ServletInvocableHandlerMethod來重寫ServletInvocableHandlerMethod#doInvoke

查找RequestMappingHandlerAdapter實(shí)例如果被創(chuàng)建

還是兩種方式,如果下載了對(duì)應(yīng)的包源碼直接ctrl點(diǎn)擊,或者debug到。

// 很明顯的一點(diǎn)是這個(gè)類是啟動(dòng)時(shí)就實(shí)例化了,InitializingBean這個(gè)接口是spring的bean注入過程中的鉤子之一(大部分帶有spring鉤子的類都是啟動(dòng)時(shí)注入spring容器,真的很少很少見動(dòng)態(tài)注入)
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean

我們直接debug構(gòu)造方法


RequestMappingHandlerAdapter 構(gòu)造方法

直接來到 WebMvcConfigurationSupport#createRequestMappingHandlerAdapter
和上面的那些方法一樣,protect,非final,并且簡(jiǎn)單粗暴,看起來就是讓我們重寫的啊,那么我們就先切入這個(gè)點(diǎn)是否可以重寫


WebMvcConfigurationSupport

上面忘了說一點(diǎn),最主要的是要看每個(gè)類的注釋,尤其是類上面的注釋,多多少少可以理解作者的設(shè)計(jì)意圖,以及相關(guān)聯(lián)的其他類
WebMvcConfigurationSupport

上圖可以看到一個(gè)非常熟悉的東西,WebMvcConfigurer springmvc的各種擴(kuò)展點(diǎn)入口。既然WebMvcConfigurationSupport標(biāo)準(zhǔn)了可能有所關(guān)聯(lián),先看一眼。結(jié)果就是,沒啥可以重寫WebMvcConfigurationSupport的跡象。那么我們換個(gè)思路

如果你有下載了很多包的源碼可以看到 WebMvcConfigurationSupport#createRequestMappingHandlerAdapter已經(jīng)被重寫了


image.png

或者找到堆棧的上一個(gè)方法調(diào)用


WebMvcAutoConfiguration
        @Override
        protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
// 這里我們眼前一亮,終于找到了真正的擴(kuò)展點(diǎn)!
            if (this.mvcRegistrations != null) {
                RequestMappingHandlerAdapter adapter = this.mvcRegistrations.getRequestMappingHandlerAdapter();
                if (adapter != null) {
                    return adapter;
                }
            }
// 這里就是WebMvcConfigurationSupport#createRequestMappingHandlerAdapter 目前走的就是這個(gè)case
            return super.createRequestMappingHandlerAdapter();
        }

如下在EnableWebMvcConfiguration的構(gòu)造方法中可以看到 mvcRegistrations 我們只需要注入這個(gè)實(shí)現(xiàn)即可

        private final WebMvcRegistrations mvcRegistrations;

        private ResourceLoader resourceLoader;

        public EnableWebMvcConfiguration(WebMvcProperties mvcProperties, WebProperties webProperties,
                ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
                ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
                ListableBeanFactory beanFactory) {
            this.resourceProperties = webProperties.getResources();
            this.mvcProperties = mvcProperties;
            this.webProperties = webProperties;
            this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
            this.beanFactory = beanFactory;
        }

我們的重寫鏈路已經(jīng)浮現(xiàn)!WebMvcRegistrations(子類)#getRequestMappingHandlerAdapter返回RequestMappingHandlerAdapter(子類) --> RequestMappingHandlerAdapter(子類)#createInvocableHandlerMethod返回ServletInvocableHandlerMethod(子類) --> ServletInvocableHandlerMethod(子類)重寫 doInovke方法即可

下面看代碼

定義一個(gè)觀察者接口由客戶端程序可插入邏輯




import org.springframework.web.method.HandlerMethod;

/**
 * @author ASDL3DL00676
 */
public interface ServletInvocableHandlerMethodInterceptor extends Ordered {

  /**
   * 通過參數(shù)來決定是否執(zhí)行
   *
   * @param args args
   * @param handlerMethod 目標(biāo)方法
   * @return boolean
   */
  boolean supports(HandlerMethod handlerMethod, Object[] args);

  /**
   * 需要目標(biāo)方法考慮異常
   * 前置方法
   *
   * @param args args
   * @param handlerMethod 目標(biāo)方法
   */
  default void beforeInvoke(HandlerMethod handlerMethod, Object[] args) {

  }


  /**
   * 異常時(shí)
   *
   * @param args args
   * @param throwable 拋出的異常
   * @param handlerMethod 目標(biāo)方法
   */
  default void afterThrows(HandlerMethod handlerMethod, Object[] args, Throwable throwable) {

  }

  /**
   * 成功時(shí)需要目標(biāo)方法考慮異常
   *
   * @param args args
   * @param result 結(jié)果值
   * @param handlerMethod 目標(biāo)方法
   */
  default void afterSuccess(HandlerMethod handlerMethod, Object[] args, Object result) {

  }

  /**
   * 函數(shù)式包裹
   *
   * @param target 目標(biāo)方法
   * @param handlerMethod controller 代理的方法
   * @param args 參數(shù)
   * @return 結(jié)果
   */
  default Supplier<Object> wrapperDoInvoke(Supplier<Object> target, HandlerMethod handlerMethod,
      Object[] args) {
    return target;
  }

  /**
   * 默認(rèn)排序
   *
   * @return int
   */
  @Override
  default int ordered() {
    return 0;
  }

}

初始化WebMvcRegistrations 注入spring

  @Bean
// 同時(shí)允許依賴當(dāng)前jar的業(yè)務(wù)服務(wù)可以繼續(xù)重寫,如果想要保留當(dāng)前jar的邏輯需要繼承這里的WebMvcRegistrationsSimpleImpl
  @ConditionalOnMissingBean
  public WebMvcRegistrations customWebMvcRegistrations(
      @Autowired(required = false) List<ServletInvocableHandlerMethodInterceptor> interceptors) {
    if (CollectionUtils.isNotEmpty(interceptors)) {
      interceptors.sort(Comparator.comparing(ServletInvocableHandlerMethodInterceptor::ordered));
    }
    return new WebMvcRegistrationsSimpleImpl(interceptors);
  }

重寫WebMvcRegistrations 并帶上觀察者



import java.util.List;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

/**
 * @author ASDL3DL00676
 */
@RequiredArgsConstructor
public class WebMvcRegistrationsSimpleImpl implements WebMvcRegistrations {
  
  @Nullable
  private final List<ServletInvocableHandlerMethodInterceptor> interceptors;

  /**
   * Return the custom {@link RequestMappingHandlerAdapter} that should be used and
   * processed by the MVC configuration.
   *
   * @return the custom {@link RequestMappingHandlerAdapter} instance
   */
  @Override
  public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
    return new RequestMappingHandlerInterceptorImpl(interceptors);
  }


}

重寫RequestMappingHandlerAdapter 并帶上觀察者



import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;

/**
 * @author ASDL3DL00676
 */
@RequiredArgsConstructor
public class RequestMappingHandlerInterceptorImpl extends RequestMappingHandlerAdapter {

  @Nullable
  private final List<ServletInvocableHandlerMethodInterceptor> interceptors;
  @Nonnull
  @Override
  protected ServletInvocableHandlerMethod createInvocableHandlerMethod(@Nonnull HandlerMethod handlerMethod) {
    return new ServletInvocableHandlerMethodWithPlugin(handlerMethod, interceptors);
  }

}

重寫ServletInvocableHandlerMethod 并帶上觀察者,對(duì)doInvoke方法提供多種攔截,以及函數(shù)包裹邏輯


import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;


/**
 * @author ASDL3DL00676
 */
@EqualsAndHashCode(callSuper = true)
public class ServletInvocableHandlerMethodWithPlugin extends ServletInvocableHandlerMethod {


  @javax.annotation.Nullable
  private final List<ServletInvocableHandlerMethodInterceptor> interceptors;

  private final HandlerMethod currentMethod;


  public ServletInvocableHandlerMethodWithPlugin(
      HandlerMethod handlerMethod,
      @Nullable List<ServletInvocableHandlerMethodInterceptor> interceptors) {
    super(handlerMethod);
    this.interceptors = interceptors;
    this.currentMethod = handlerMethod;
  }

  @Nullable
  @Override
  public Object invokeForRequest(
      @Nonnull NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      @Nonnull Object... providedArgs) throws Exception {
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
    }
    try {
      Supplier<Object> target = () -> doInvoke(args);
      if (CollectionUtils.isNotEmpty(interceptors)) {
        for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
          if (interceptor.supports(currentMethod, args)) {
            interceptor.beforeInvoke(currentMethod, args);
            target = interceptor.wrapperDoInvoke(target, currentMethod, args);
          }
        }
      }
      Object result = target.get();
      if (CollectionUtils.isNotEmpty(interceptors)) {
        for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
          if (interceptor.supports(currentMethod, args)) {
            interceptor.afterSuccess(currentMethod, args, result);
          }
        }
      }
      return result;
    } catch (Throwable throwable) {
      if (CollectionUtils.isNotEmpty(interceptors)) {
        for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
          if (interceptor.supports(currentMethod, args)) {
            interceptor.afterThrows(currentMethod, args, throwable);
          }
        }
      }
      throw throwable;
    }
  }


}

到此我們找到了擴(kuò)展點(diǎn),并通過重寫對(duì)doInvoke添加觀察者,函數(shù)式包裹等,也可以通過其他的擴(kuò)展方法對(duì)

ServletInvocableHandlerMethod ,RequestMappingHandlerAdapter進(jìn)行擴(kuò)展

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容