SpringMVC原始碼解析系列4-HandleAdapter

hahaeee發表於2018-03-22

SpringMVC原始碼解析系列3-HandleAdapter

整體關係

HandleAdapter介面定義

定義: 根據HandlerMethod資訊,對http請求進行引數解析,並完成呼叫

先看一下HandlerAdapter的介面定義

public interface HandlerAdapter {
   //判斷是否支援該handler型別的解析
   boolean supports(Object handler);
   //引數解析 並呼叫handler完成過程呼叫 
   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
   //用於處理http請求頭中的last-modified
   long getLastModified(HttpServletRequest request, Object handler);

}

複製程式碼

HandlerAdapter實現關係

RequestMappingHandlerAdapter

以RequestMappingHandlerAdapter為例來講,先看下繼承關係

RequestMappingHandlerAdapter

初始化過程

同RequestMappingHandlerMapping都有ApplicationContextAware,ServletContextAware,InitializingBean三個生命週期方法

這裡我們就直接看InitializingBean了

#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Override
public void afterPropertiesSet() {
 // 1.裝載@ControllerAdvice註解的類
 initControllerAdviceCache();
// 2.裝載ArgumentResolver(預設+自定義)
 if (this.argumentResolvers == null) {
    List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    //包裝成一個Composite物件
    this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
 }
 // 2.裝載InitBinderArgumentResolvers(預設+自定義)
 if (this.initBinderArgumentResolvers == null) {
    List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
    //包裝成一個Composite物件
    this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
 }
 // 3.裝載ReturnValueHandlers(預設+自定義)
 if (this.returnValueHandlers == null) {
    List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    //包裝成一個Composite物件
    this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
 }
}
複製程式碼

過程概括:

  1. 裝載帶有ControllerAdvices註解的物件

    private void initControllerAdviceCache() {
      //從容器中獲取所有帶有ControllerAdvices註解的類名 幷包裝成ControllerAdviceBean
      List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
      OrderComparator.sort(beans);
      List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
      for (ControllerAdviceBean bean : beans) {
        //篩選出帶有@ModelAttribute且不帶@RequestMapping註解的方法
        Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
          //儲存到modelAttributeAdviceCache中
          this.modelAttributeAdviceCache.put(bean, attrMethods);
        }
        //篩選出帶InitBinder註解的方法 新增到initBinderAdviceCache中
        Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
          this.initBinderAdviceCache.put(bean, binderMethods);
        }
        //篩選實現RequestBodyAdvice介面 新增到requestResponseBodyAdviceBeans中
        if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
          requestResponseBodyAdviceBeans.add(bean);
          if (logger.isInfoEnabled()) {
            logger.info("Detected RequestBodyAdvice bean in " + bean);
          }
        }
        //篩選實現ResponseBodyAdvice介面 新增到requestResponseBodyAdviceBeans中
        if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
          requestResponseBodyAdviceBeans.add(bean);
          if (logger.isInfoEnabled()) {
            logger.info("Detected ResponseBodyAdvice bean in " + bean);
          }
        }
      }
      //儲存到全域性變數
      if (!responseBodyAdviceBeans.isEmpty()) {
        this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
      }
    }
    複製程式碼
    • 篩選出帶有@ModelAttribute且不帶@RequestMapping註解的方法
    • 篩選出帶InitBinder註解的方法 新增到initBinderAdviceCache中
    • 篩選實現RequestBodyAdvice介面 新增到responseBodyAdvice中
    • 篩選實現ResponseBodyAdvice介面 新增到responseBodyAdvice中
  2. 裝載ArgumentResolvers(預設+自定義)

  3. 裝載InitBinderArgumentResolvers(預設+自定義)

  4. 裝載ReturnValueHandlers(預設+自定義)

自定義擴充方式放後面說

以下為HandlerAdapter預設解析器

RequestHandlerAdapter

HandlerMethodReturnValueHandler,HandlerMethodArgumentResolver

//引數解析器
public interface HandlerMethodArgumentResolver {
  //判斷是否支援該引數的解析(根據型別,註解等)
   boolean supportsParameter(MethodParameter parameter);
  //對引數進行解析 得到解析結果
   Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
//返回值解析器
public interface HandlerMethodReturnValueHandler {
  //判斷是否支援該返回值的解析(根據型別,註解等)
	boolean supportsReturnType(MethodParameter returnType);
  //對返回值進行解析
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

複製程式碼

DispatcherServlet呼叫HandlerAdapter過程

//1.呼叫support()方法判斷是否支援HandlerMethod的解析
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果是Get或Head請求 呼叫getLastModified()獲取上次更新時間 
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
   long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
   if (logger.isDebugEnabled()) {
      logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
   }
  //如果小於瀏覽器快取更新時間 則直接返回 瀏覽器使用本地快取
   if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
      return;
   }
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
複製程式碼

過程總結:

  1. 呼叫support()方法判斷是否支援改handler的解析

    #org.springframework.web.servlet.DispatcherServlet
    //在doDispatch()方法中呼叫了getHandlerAdapter(Object)方法來得到一個HandlerAdapter
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        //呼叫HandlerAdapter.support()方法 判斷是否支援該handler物件的解析
       for (HandlerAdapter ha : this.handlerAdapters) {
            ...
          if (ha.supports(handler)) {
             return ha;
          }
       }
      ...
    }
    #org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
    //根據handler物件判斷是否支援handler解析
    @Override
    public final boolean supports(Object handler) {
    	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
    #org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
    	return true;
    }
    複製程式碼

  2. 如果是Get或Head請求 呼叫getLastModified()獲取上次更新時間

    如果小於瀏覽器快取更新時間 則直接返回 瀏覽器使用本地快取

    #org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    @Override
    protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
       return -1;
    }
    複製程式碼

  3. 呼叫handler()方法完成過程呼叫(引數解析 返回值解析)

    #org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
          throws Exception {
       return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    #org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
    		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    	//對http協議快取方面的請求頭的處理(expire,cache-control)
    	if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    		checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    	}else {
    		checkAndPrepare(request, response, true);
    	}
    	//是否使用session鎖
    	if (this.synchronizeOnSession) {
    		HttpSession session = request.getSession(false);
    		if (session != null) {
              	//得到互斥量
    			Object mutex = WebUtils.getSessionMutex(session);
    			synchronized (mutex) {//執行過程呼叫
    				return invokeHandleMethod(request, response, handlerMethod);
    			}
    		}
    	}
    	//執行過程呼叫(引數解析 過程呼叫)
    	return invokeHandleMethod(request, response, handlerMethod);
    }
    複製程式碼

引數解析,過程呼叫流程分析

//根據HandlerMethod解析引數 並完成過程呼叫得到一個ModelAndView
private ModelAndView invokeHandleMethod(HttpServletRequest request,
                 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  try {
    //對@InitBinder的處理 主要是聚合了@InitBinder的所有處理方法
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    //@ModelAttribute的處理
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
	//對HandlerMethod的裝飾,主要是增加了引數解析和返回值轉化的功能
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    //提供對引數解析的支援
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    //提供對返回值解析的支援
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    //提供對@InitBinder處理的支援
    invocableMethod.setDataBinderFactory(binderFactory);
    //TODO 尚不明功能
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
	
    //可以看做handler()過程的上下文
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
==========================非同步處理分割線=============	
	//AsyncWebRequest內部持有AsyncContext 可以通過其開啟非同步任務
    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);
	//非同步處理Manager
    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();
      ...
      //替換invocableMethod(原先非同步處理的方法返回值是Callable現在直接返回結果)
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }
	//對invocableMethod進行引數解析,過程呼叫,返回值轉化
    //並將結果存到mavContainer中
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    //如果非同步處理正在執行(已經開始,尚未結束) 立刻返回
    //同時DispatcherServlet也直接返回 等待AsyncContext.dispatch()呼叫再次進入doDispatch()方法
    if (asyncManager.isConcurrentHandlingStarted()) {
      return null;
    }
	//從mavContainer撈出結果
    return getModelAndView(mavContainer, modelFactory, webRequest);
  }
  finally {
    webRequest.requestCompleted();
  }
}
複製程式碼

以上是invokeHandleMethod()的完整過程

但在呼叫過程中實際從非同步處理分割線開始分為2種情況:

  1. 同步呼叫: 過程比較簡單,直接進行引數解析和返回值轉化就好了

    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    return getModelAndView(mavContainer, modelFactory, webRequest);
    複製程式碼
  2. 非同步呼叫:

    再分為兩種情況:

    1. 非同步處理中(已開始,尚未完成)

      //AsyncWebRequest內部持有AsyncContext 可以通過其開啟非同步任務
      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);
      //非同步處理Manager
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      //設定非同步執行執行緒池
      asyncManager.setTaskExecutor(this.taskExecutor);
      //提供對非同步處理的支援
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      //非同步呼叫攔截器
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
      //對invocableMethod進行引數解析,過程呼叫(呼叫AsyncWebRequest.startAsync()執行非同步過程),返回值轉化
      //並將結果存到mavContainer中
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      //如果非同步處理正在執行(已經開始,尚未結束) 立刻返回
      //同時DispatcherServlet也直接返回 
      return null;
      
      #org.springframework.web.servlet.DispatcherServlet
      protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ...
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }
        ...
      }
      //等待AsyncWebRequest.dispatch()被呼叫 然後再次進入doDispatch()方法
      
      複製程式碼
    2. 非同步執行完成,再次進入doDispatch()流程

      //AsyncWebRequest內部持有AsyncContext 可以通過其開啟非同步任務
      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);
      //非同步處理Manager
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      //設定非同步執行執行緒池
      asyncManager.setTaskExecutor(this.taskExecutor);
      //提供對非同步處理的支援
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      //非同步呼叫攔截器
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
      //非同步處理完成 獲取非同步執行結果
      Object result = asyncManager.getConcurrentResult();
      mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
      asyncManager.clearConcurrentResult();
      //替換invocableMethod(原先非同步處理的方法返回值是Callable現在直接返回結果)
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
      //對invocableMethod進行引數解析,過程呼叫,返回值轉化
      //並將結果存到mavContainer中
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      //從mavContainer撈出結果
      return getModelAndView(mavContainer, modelFactory, webRequest);
      複製程式碼

ServletInvocableHandlerMethod

先看一下ServletInvocableHandlerMethod是個什麼東東

ServletInvocableHandlerMethod

HandlerMethod:儲存了處理過程方法

InvocableHandlerMethod: 對HandlerMethod的裝飾,增加了引數解析的功能

ServletInvocableHandlerMethod:對HandlerMethod的裝飾,增加了返回值轉化的功能

同步流程

  1. invocableMethod.invokeAndHandle(webRequest, mavContainer);

    #org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
    public void invokeAndHandle(ServletWebRequest webRequest,
                        ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
      // 1.1 引數解析 並完成過程呼叫
      Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
      setResponseStatus(webRequest);
      ...
        try {
          //1.2 使用returnValueHandlers對返回結果進行處理 講結果塞到mavContainer中 過程類似引數解析
          this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
    }
    #org.springframework.web.method.support.InvocableHandlerMethod
    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
      //1.1.1 引數解析並得到繫結的結果
      Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
      ...
        //1.1.2 反射完成過程呼叫 
        Object returnValue = doInvoke(args);
      ...
        return returnValue;
    }
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                             Object... providedArgs) throws Exception {
      //引數資訊
      MethodParameter[] parameters = getMethodParameters();
      Object[] args = new Object[parameters.length];
      for (int i = 0; i < parameters.length; i++) {
        //呼叫HandlerMethodArgumentResolver#supportsParameter判斷是否支援該引數的解析
        if (this.argumentResolvers.supportsParameter(parameter)) {
          try {
            //呼叫HandlerMethodArgumentResolver#resolveArgument進行解析
            args[i] = this.argumentResolvers.resolveArgument(
              parameter, mavContainer, request, this.dataBinderFactory);
            continue;
          }
          ...
        }
        ...
      }
      return args;
    }
    複製程式碼

  2. 包裝ModelAndView getModelAndView(mavContainer, modelFactory, webRequest);

    //從mavContainer取出結果 包裝成ModelAndView
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                         ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
      modelFactory.updateModel(webRequest, mavContainer);
      if (mavContainer.isRequestHandled()) {
        return null;
      }
      ModelMap model = mavContainer.getModel();
      ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
      if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
      }
      //如果是redirect請求
      if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
      return mav;
    }
    複製程式碼

到此 HandlerAdapter.handler的呼叫過程算分析完了

@InitBinder的處理

WebDataBinder:資料(物件屬性)繫結器,用於對物件的屬性進行轉化(Formatter)和校驗(Validator)

InitBinder: @InitBinder註解用於在引數解析前初始化WebDataBinder.簡單來說就是可以對WebDataBinder增加Validator和屬性轉化器Formatter

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InitBinder {
	//在哪些屬性上發生作用
	String[] value() default {};
}
複製程式碼

示例

@InitBinder
protected void initBinder(WebDataBinder binder) {
 //新增自定義Data型別Formatter
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  dateFormat.setLenient(false);
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

複製程式碼

@InitBinder方法分為兩種:

  • 全域性@InitBinder:定義在註解有@ControllerAdvices的類中,裝載發生在HandlerAdapter初始化呼叫initControllerAdviceCache()過程中(見上文初始化過程分析)

  • 區域性@InitBinder:定義在註解有@Controller的類中,裝載發生在HandlerAdapter.handler()呼叫getDataBinderFactory()過程中

    private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
      //獲取處理方法所在的類
      Class<?> handlerType = handlerMethod.getBeanType();
      //從儲存的全域性快取中找屬於該類的區域性@InitBinder方法
      Set<Method> methods = this.initBinderCache.get(handlerType);
      if (methods == null) {//如果沒找到 說明該類不是一個@ControllerAdvices註解的類(只有@Controller沒有@ControllerAdvices)
        //得到該類中所有@InitBinder註解的方法
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        //將區域性@InitBinder方法快取到initBinderCache中
        this.initBinderCache.put(handlerType, methods);
      }
      List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
      //將全域性@InitBinder方法包裝成InvocableHandlerMethod,新增到initBinderMethods
      for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
        if (entry.getKey().isApplicableToBeanType(handlerType)) {
          Object bean = entry.getKey().resolveBean();
          for (Method method : entry.getValue()) {
            initBinderMethods.add(createInitBinderMethod(bean, method));
          }
        }
      }
      //將區域性@InitBinder方法包裝成InvocableHandlerMethod,新增到initBinderMethods
      for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
      }
      //使用initBinderMethods物件(區域性+全域性),包裝成WebDataBinderFactory物件
      return createDataBinderFactory(initBinderMethods);
    }
    protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
      throws Exception {
      return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
    }
    
    複製程式碼

看完了了@InitBinder的裝載過程,來看下@InitBinder方法是怎樣被處理的

處理過程

this.argumentResolvers.resolveArgument(
          parameter, mavContainer, request, this.dataBinderFactory);
複製程式碼

可以看到在使用argumentResolvers.resolveArgument()進行引數解析時將dataBinderFactory作為引數傳遞了過去

分別來看RequestParamMapMethodArgumentResolver,RequestResponseBodyMethodProcessor,ModelAttributeMethodProcessor三個引數解析器是如何處理@InitBinder方法的

  • RequestParamMapMethodArgumentResolver

    @Override
    	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
          // 完全沒使用binderFactory
          ...
    	}
    }
    複製程式碼

    因為說過@InitBinder是用來初始化WebDataBinder的,而RequestParamMapMethodArgumentResolver是處理表單屬性的(不是物件),所以完全沒用

  • RequestResponseBodyMethodProcessor

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
      parameter = parameter.nestedIfOptional();
     //使用binderFactory建立物件解析器
      WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
      if (arg != null) {
        //使用物件解析進行引數校驗
        validateIfApplicable(binder, parameter);
        //如果引數校驗異常,且目標方法引數列表中沒有BindingResult型別引數,則直接丟擲引數解析異常
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) 	{
        throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
        }
      }
      //將引數解析結果存到mavContainer上下文中
      mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
      return adaptArgumentIfNecessary(arg, parameter);
    }
    
    複製程式碼

    因為RequestResponseBodyMethodProcessor將屬性繫結委託給了json解析器,所以這裡WebDataBinder只負責引數校驗

  • ModelAttributeMethodProcessor

    @Override
    public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer 		mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    
      String name = ModelFactory.getNameForParameter(parameter);
      Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
                          createAttribute(name, parameter, binderFactory, webRequest));
    	...
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
        //使用binder進行屬性繫結
        if (!mavContainer.isBindingDisabled(name)) {
          bindRequestParameters(binder, webRequest);
        }
        //使用binder進行屬性校驗
        validateIfApplicable(binder, parameter);
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
          throw new BindException(binder.getBindingResult());
        }
      }
    
      // Add resolved attribute and BindingResult at the end of the model
      Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
      mavContainer.removeAttributes(bindingResultModel);
      mavContainer.addAllAttributes(bindingResultModel);
    
      return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
    }
    protected Object createAttribute(String attributeName, MethodParameter methodParam,
    			WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
      return BeanUtils.instantiateClass(methodParam.getParameterType());
    }
    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
      ((WebRequestDataBinder) binder).bind(request);
    }
    複製程式碼

    使用WebDataBinder進行了屬性繫結和屬性校驗

相關文章