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);
}
複製程式碼
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);
}
}
複製程式碼
過程概括:
-
裝載帶有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中
-
裝載ArgumentResolvers(預設+自定義)
-
裝載InitBinderArgumentResolvers(預設+自定義)
-
裝載ReturnValueHandlers(預設+自定義)
自定義擴充方式放後面說
以下為HandlerAdapter預設解析器
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());
複製程式碼
過程總結:
-
呼叫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; } 複製程式碼
-
如果是Get或Head請求 呼叫getLastModified()獲取上次更新時間
如果小於瀏覽器快取更新時間 則直接返回 瀏覽器使用本地快取
#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter @Override protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) { return -1; } 複製程式碼
-
呼叫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種情況:
-
同步呼叫: 過程比較簡單,直接進行引數解析和返回值轉化就好了
invocableMethod.invokeAndHandle(webRequest, mavContainer); return getModelAndView(mavContainer, modelFactory, webRequest); 複製程式碼
-
非同步呼叫:
再分為兩種情況:
-
非同步處理中(已開始,尚未完成)
//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()方法 複製程式碼
-
非同步執行完成,再次進入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是個什麼東東
HandlerMethod:儲存了處理過程方法
InvocableHandlerMethod: 對HandlerMethod的裝飾,增加了引數解析的功能
ServletInvocableHandlerMethod:對HandlerMethod的裝飾,增加了返回值轉化的功能
同步流程
-
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; } 複製程式碼
-
包裝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進行了屬性繫結和屬性校驗