SpringMVC 執行流程源碼解析&自定義實現攔截器

1、控制器實現方式&對應的處理器適配器

不一樣的實現方式調用不一樣的 HandlerAdapter
  • 1.實現 Controller 接口 --> SimpleControllerHandlerAdapter
  • 2.實現 HttpRequestHandler 接口 --> HttpRequestHandlerAdapter
  • 3.經過 @Controller 註解 --> RequestMappingHandlerAdapter

2、執行流程

3、組件介紹

  • DispatcherServlet

前端控制器,用於接收請求,處理響應結果前端

  • HandlerMapping

處理器映射器,根據請求URL,找到對應的Handler。 其實就是HandlerExecutionChain ,HandlerExecutionChain 又包括了 - Handler 和 HandlerInterceptorweb

  • HandlerAdapter

處理器適配器,用於調用處理器(Handler|Controller)的方法,這是個接口,會根據不一樣的接口實現調用不一樣的處理器適配器。數組

  • HandlerInterceptor

處理器攔截器,自定義攔截器要實現該接口或者實現該接口的子類,根據業務實現 preHandle()、postHandle()、afterCompletion() 方法app

  • Handler

處理器Handler又名Controller,用於接收用戶請求數據,調用業務方法處理請求ide

4、源碼解析

從DispatcherServlet 方法開始分析,在此以前 servlet 會調用 onRefresh()、doService(),上述流程圖就是從doService() --> doDispatch() 方法開始的post

/**
 * 上述流程圖的核心代碼,去除了源碼中的其餘邏輯
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {

         // 獲取處理器執行調用鏈 HandlerExecutionChain,包括處理器 Controller 和攔截器 HandlerInterceptor
         // Determine handler for the current request.
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
        
         // 獲取相應的處理器適配器,也被初始化過了
         // Determine handler adapter for the current request.
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
         // 調用攔截器的 preHandle()方法
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         
         // 真正的調用Controller 中的方法,handle() 是個抽象方法,根據不一樣的處理器調用
         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         //調用攔截器的 hostHandle 方法,和調用 preHandle() 處理方式同樣,就再也不分析了
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
     
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
 
}
獲取處理器執行調用鏈對象
/**
 * this.handlerMappings 在此以前已經被初始化完成,這裏直接根據 request 獲取
 */
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}
onRefresh 方法由 Servlet 調用,用來初始化處理請求用到的組件
@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   //初始化處理器執行調用鏈
   initHandlerMappings(context);
   //初始化處理器適配器
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}
調用攔截器的 preHandle()方法
/**
 * 循環遍歷攔截器數組中攔截器的 preHandle()方法,true-放行,false-請求被攔截
 */
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      for (int i = 0; i < interceptors.length; i++) {
         HandlerInterceptor interceptor = interceptors[i];
         if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
         }
         this.interceptorIndex = i;
      }
   }
   return true;
}
以RequestMappingHandlerAdapter處理器適配器的 handler()方法分析
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	checkRequest(request);
        //...省略其餘處理邏輯
            
        //真正執行 Controller 中的方法的代碼,返回 ModelAndView 對象  
        mav = invokeHandlerMethod(request, response, handlerMethod);
	
	return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
      // 執行處理器的方法,
      //invocableMethod 就是映射過來的 Controller 類中對應的方法com.xxx.controller.XxxController#xxx()    
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   
}
@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));
   }
   //終於要執行 Controller 的方法了
   return doInvoke(args);
}

5、自定義實現攔截器

  • 第一步:實現一個攔截器HandlerInterceptor,根據業務實現 preHandle()、postHandle()、afterCompletion() 方法
//我這隻實現了 preHandle()方法,true-放行,false-請求直接返回
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=============執行攔截器 preHandle()================");
        return true;
    }
}
  • 第二步:把攔截器注入到 Spring 容器
/**
 * 注入的方式不少,你能夠在MyInterceptor 上加@Component 註解,不用@Bean 的方式
 */
@Configuration
public class WebAppConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor())
                //添加攔截的 url,/**表示匹配全部,/* 表示只匹配一層
                .addPathPatterns("/**")
                //添加放行的 url
                .excludePathPatterns("/exclude/**");
    }
}
相關文章
相關標籤/搜索