JavaWeb學習之——Spring篇之HandlerAdapter(未完)

1. 概述

handlerAdpater的實現類總共有5個(其中一個已經廢棄),並且層級都不多。web

public interface HandlerAdapter {

   boolean supports(Object handler);

   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

   long getLastModified(HttpServletRequest request, Object handler);

}

handlerAdapter的接口很簡單,只有3個方法。緩存

  • support——判斷handler是否適用於當前HandlerAdapter
  • handler——根據handler返回ModelAndView
  • getLastModified——看名字就知道就是返回最後修改時間

2. HttpRequestHandlerAdapter、SimpleServletHandlerAdapter、SimpleControllerHandlerAdapter

這3個adapter很簡單,都是分別針對特定的handler的類型,直接調用就能夠了cookie

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ((HttpRequestHandler) handler).handleRequest(request, response);
   return null;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ((Servlet) handler).service(request, response);
   return null;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return ((Controller) handler).handleRequest(request, response);
}

3. RequestMappingHandlerAdapter

RequestMappingHandlerAdapter是AbstractHandlerMethodAdapter的惟一實現類session

這個處理就比較複雜(但正是由於他爲咱們帶來了快捷的使用方式)。但總的來講作了3件事app

  1. 準備好Handler所須要的參數
  2. 使用Handler處理request請求
  3. 把Handler處理後的結果統一轉換爲ModelAndView

3.1 準備好Handler所須要的參數

這個步驟應該是3步中最複雜的,拋開直接把Request中的參數(url中的或者body中的)之外、cookie中的參數、session中的參數,還要處理Spring MVC中的一些東西,例如FlashMap中的參數、SessionAttribute中的參數、ModelAttitude中的參數異步

能夠看出,正式由於SpringMVC爲咱們提供了各類擴展以及便捷的使用,纔會處理各類各樣的狀況。代碼雖然不少,若是你知道了用法,反過來看代碼邏輯就會輕鬆得多(用結果推緣由,也就是過後諸葛亮)。async

@InitBinder,@ModelAttribute,@ControllerAdvice,@SessionAttribute你應該知道。ide

3.2 初始化

首先看afterPropertiesSet(實現了InitializingBean接口,回在bean初始化的時候調用)函數

@Override
public void afterPropertiesSet() {
   // Do this first, it may add ResponseBody advice beans
   initControllerAdviceCache();

   if (this.argumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
      this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.initBinderArgumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
      this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.returnValueHandlers == null) {
      List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
      this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
   }
}

3.2.1 initControllerAdviceCache

這個方法能夠看出,就是初始化@ControllerAdvice註解標註的類,裏面的邏輯就是ui

  1. 找出@ModelAttribute註解(而且沒有@RequestMapping)註解的方法和@InitBinder註解的方法,分別放入modelAttributeAdviceCache和initBinderAdviceCache中
  2. 找出RequestBodyAdvice和ResponseBodyAdvice實現類,放入requestResponseBodyAdvice

核心代碼以下:

private void initControllerAdviceCache() {

   List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
   AnnotationAwareOrderComparator.sort(beans);

   List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();

   for (ControllerAdviceBean bean : beans) {
      Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
      if (!attrMethods.isEmpty()) {
         this.modelAttributeAdviceCache.put(bean, attrMethods);
      }
      Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
      if (!binderMethods.isEmpty()) {
         this.initBinderAdviceCache.put(bean, binderMethods);
      }
      if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
         requestResponseBodyAdviceBeans.add(bean);
      }
      if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
         requestResponseBodyAdviceBeans.add(bean);
      }
   }

   if (!requestResponseBodyAdviceBeans.isEmpty()) {
      this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
   }
}

其中 MODEL_ATTRIBUTE_METHODS和INIT_BINDER_METHODS是靜態類

public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
   @Override
   public boolean matches(Method method) {
      return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
            (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
   }
};
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
   @Override
   public boolean matches(Method method) {
      return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
   }
};

3.2.2 初始化ArgumentResolvers

if (this.argumentResolvers == null) {
   List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
   this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}

首先是使用getDefaultArgumentResolvers 獲取默認的ArgumentResolver類,而後封裝成一個Composite放入到argumentResolvers中。默認的ArgumentResolver有以下:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
   List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

   // 基於註釋相關參數解析器,從名字就能夠看出來分別對應哪些註解
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
   resolvers.add(new RequestParamMapMethodArgumentResolver());
   resolvers.add(new PathVariableMethodArgumentResolver());
   resolvers.add(new PathVariableMapMethodArgumentResolver());
   resolvers.add(new MatrixVariableMethodArgumentResolver());
   resolvers.add(new MatrixVariableMapMethodArgumentResolver());
   resolvers.add(new ServletModelAttributeMethodProcessor(false));
   resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new RequestHeaderMapMethodArgumentResolver());
   resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new SessionAttributeMethodArgumentResolver());
   resolvers.add(new RequestAttributeMethodArgumentResolver());

   // 基於類型的參數解析器
   resolvers.add(new ServletRequestMethodArgumentResolver());
   resolvers.add(new ServletResponseMethodArgumentResolver());
   resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RedirectAttributesMethodArgumentResolver());
   resolvers.add(new ModelMethodProcessor());
   resolvers.add(new MapMethodProcessor());
   resolvers.add(new ErrorsMethodArgumentResolver());
   resolvers.add(new SessionStatusMethodArgumentResolver());
   resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

   // 自定義的參數解析器
   if (getCustomArgumentResolvers() != null) {
      resolvers.addAll(getCustomArgumentResolvers());
   }

   // 針對全部類型的參數解析器
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
   resolvers.add(new ServletModelAttributeMethodProcessor(true));

   return resolvers;
}

加載的默認解析器不少,主要分爲4類

  1. 針對註解的參數解析器,根據名字就能夠知道是針對那些哪些註解
  2. 基於參數類型的解析器(例如,參數裏面的HttpServletRequest,HttpSession,Model等)
  3. 自定義參數解析器,用戶能夠本身寫一個resolver,而後注入到customArgumentResolvers這個參數中。不過因爲自定義的解析器順序是在上面2種解析器後面,若是一、2已經解析成功的,就不會再使用自定義的解析器
  4. 針對全部類型的解析器(例如@RequestParamter註解)
if (this.initBinderArgumentResolvers == null) {
   List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
   this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
   List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
   this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}

和上面相似,就不在贅述

3.3 調用

RequestMappingHandlerAdapter實現了HandlerAdapter,其中supports和getLastModified的方法很簡單,重點是看

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

這個接口。

這個接口的實現是在handleInternal函數中

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   //判斷request參數,1. 判斷method是否一致(GET,POST),若是須要session判斷是否有session
   checkRequest(request);

   // 是不是同步的,默認是false
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            //調用handler
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
   
   // 若是response頭裏面沒有Cache-Control,而且該handler對應的類有@SessionAttributes,則
   // 設置Cache-Control爲non—cache
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      //使用HandlerMethod.getBeanType()做爲key,在緩存sessionAttributesHandlerCache。
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}

這個最主要的方法就是invokeHandlerMethod,這個方法就包含了參數綁定、執行Handler、處理返回值的整個流程,主要步驟註釋上都有標明。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   //首先是利用HttpServletRequest和HttpServletResponse封裝成ServletWebRequest,在參數解析器中入參就是這個類。
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
      //@InitBinder註解的方法獲取(先從緩存獲取,不然從類定義中獲取放入緩存),
      //最後利用獲取的數據建立WebDataBinderFactory 
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      //處理@SessiohAttributes和@ModeAttribute
      //看名字就是是爲Model服務的,例如@SessiohAttributes和
      //@ModeAttribute(包括全局和局部,具體看初始化流程)註解參數加入的model中等
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
       
      //ServletInvocableHandlerMethod繼承InvocableHandlerMethod(實現了HandlerMethod接口)。
      //實際處理請求就是經過這個類執行,包括參數綁定、請求處理調用和返回值。
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      // 設置參數解析器
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      // 設置返回值處理器,封裝成ModelAndView
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      // InitBinder相關函數
      invocableMethod.setDataBinderFactory(binderFactory);
      // 參數查詢
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
       
      // 新建ModelAndViewContainer 
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      // 設置falshMap
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      // 將@SessionAttributes和@ModelAttribute的相關屬性設置到Model中
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      //後面詳解
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
      
      // 異步處理請求,後面詳解
      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);

      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

      if (asyncManager.hasConcurrentResult()) {
         Object result = asyncManager.getConcurrentResult();
         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
         asyncManager.clearConcurrentResult();
         if (logger.isDebugEnabled()) {
            logger.debug("Found concurrent result value [" + result + "]");
         }
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }

      //實際調用
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

      //更新model信息,
      // 這裏面須要注意,若是@SessionStatus若是Complate爲true,則會清除,這裏須要注意的是清除實在Handler處理完後才調用。
      // 根據mavContainer建立ModelAndView(若是有RedirectAttributes,設置FlashMap)
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}

要理解整個處理流程,還須要對方法中涉及的一些對象進行分析,下面就會對ModelAndViewContainer、ModelFactory等類進行分析

4.  ModelAndViewContainer

ModelAndViewContainer就是負責保存Model、View等等功能

private boolean ignoreDefaultModelOnRedirect = false;

// view對象,也能夠是String類型的邏輯視圖
private Object view;

// 默認Model
private final ModelMap defaultModel = new BindingAwareModelMap();

//redirect Model
private ModelMap redirectModel;

//返回Redirect 視圖表示
private boolean redirectModelScenario = false;

//SessionAttribute使用完成標誌
private final SessionStatus sessionStatus = new SimpleSessionStatus();

//請求是否完成標誌
private boolean requestHandled = false;
  • 默認視圖使用的是BindingAwareModelMap這個類,同時繼承了Model和ModelMap這2個類(所以在參數裏設置 Model 或者ModelMap 都是同一個對象)
  • 若是是redirect視圖,那麼defaultModel中的參數就沒有任何用處,同理非redirect視圖的Redirect Model也是沒用的

這個類的最主要的方法是getModel()

public ModelMap getModel() {
   if (useDefaultModel()) {
      return this.defaultModel;
   }
   else {
      if (this.redirectModel == null) {
         this.redirectModel = new ModelMap();
      }
      return this.redirectModel;
   }
}
private boolean useDefaultModel() {
   return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}

從代碼裏面看出,使用defaultModel的狀況有:一、返回非redirect視圖。二、返回的雖然是redirect視圖,但redirectMap不爲空而且ignoreDefaultModelOnRedirect爲false的狀況

5. ModelFactory

5.1 SessionAttributesHandler

在說ModelFactory以前必須先了解SessionAttributesHandler。SessionAttributesHandler就是用於處理@SessionAttitudes註解的。

@SessionAttitudes的功能簡單來講,就是把一些數據在放入Model的時候保存起來,而後在其餘Handler中的Model方法中能夠獲取。至關於用session來傳遞參數。只不過咱們能夠方便的一鍵刪除這些。

SessionAttributesHandler中的屬性有:

// 保存@SessionAttributes註解裏的value
private final Set<String> attributeNames = new HashSet<String>();
// 保存@SessionAttributes註解裏的types
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
// 保存@SessionAttributes中當前已知的Session Name
private final Set<String> knownAttributeNames =
      Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(4));
// 實際保存數據的處理類,可是數據並非在這個類裏,默認是保存在Session中
private final SessionAttributeStore sessionAttributeStore;

除此以外,還提供了而且提供增刪查的功能。查詢全部SessionAttribute和刪除全部SessionAttribute都是刪除的是knownAttributeNames中的

5.2 ModelFactory 初始化

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
      HandlerMethod handlerMethod) throws Exception {
   // 第一步:獲取全部SessionAttribute中的參數,merge到ModelAndViewContainer中
   Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
   container.mergeAttributes(sessionAttributes);

   // 第二步:調用@ModelAttritude註解的方法(結果加入到ModelAndViewContainer中)
   invokeModelAttributeMethods(request, container);

   // 第三步:處理使用@ModelAttribute註解的參數(須要在@SessionAttribute中存在),最後加入到ModelAndViewContainer
   for (String name : findSessionAttributeArguments(handlerMethod)) {
      if (!container.containsAttribute(name)) {
         Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
         if (value == null) {
            throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
         }
         container.addAttribute(name, value);
      }
   }
}
  • 第一步比較簡單,直接從上面介紹過的SessionAttributesHandler中獲取全部SessionAttribute參數。而後merge到ModelAndViewContainer(不覆蓋原有的)
  • 第二步就是調用invokeModelAttributeMethods處理@ModelAttritude註解的方法. 重要說明見註釋
  • private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
          throws Exception {
    
       while (!this.modelMethods.isEmpty()) {
          InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
          ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
          if (container.containsAttribute(ann.name())) {
             if (!ann.binding()) {
                container.setBindingDisabled(ann.name());
             }
             continue;
          }
          // 根據request調用HandlerMethod,須要說明的是其中的參數使用了ArgumentResolver,所以注入的Model
          // 就是container中的model(session,httpRequest相似也有對用的ArgumentResolver)
          Object returnValue = modelMethod.invokeForRequest(request, container);
    
          // 如何返回的參數不爲空,則把參數設置到model中
          if (!modelMethod.isVoid()){
             // 優先使用註解中的名字,沒有則根據返回的class類型設置名字
             String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
             if (!ann.binding()) {
                container.setBindingDisabled(returnValueName);
             }
             if (!container.containsAttribute(returnValueName)) {
                container.addAttribute(returnValueName, returnValue);
             }
          }
       }
  • 第三步:處理使用@ModelAttribute註解的參數,前提是SessionAttribute中有,而且Model中並不存在纔會加入。

所以,分析到這裏,Model中設置參數的優先級就知道了

FlashMap>SessionAttribute>ModelAttribute方法(全局優先於局部)>ModelAttribute參數(全局優先於局部)

未完待續

相關文章
相關標籤/搜索