SpringMVC源碼解析系列4-HandleAdapter

SpringMVC源碼解析系列3-HandleAdapter

總體關係

HandleAdapter接口定義

定義: 根據HandlerMethod信息,對http請求進行參數解析,並完成調用java

先看一下HandlerAdapter的接口定義web

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爲例來說,先看下繼承關係spring

RequestMappingHandlerAdapter

初始化過程

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

這裏咱們就直接看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註解的對象bash

    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(默認+自定義)session

  3. 裝載InitBinderArgumentResolvers(默認+自定義)mvc

  4. 裝載ReturnValueHandlers(默認+自定義)app

自定義拓展方式放後面說

如下爲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進行了屬性綁定和屬性校驗

相關文章
相關標籤/搜索