這一章我們主要是梳理下HandlerAdapter,在看這一篇的時候可以去看下前面的關于HandlerMapping與處理器映射器前置知識的内容,怎樣能更好的了解。
1、HandlerAdapter接口:
public interface HandlerAdapter { boolean supports(Object handler); @Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); }
可以看到這個接口有三個方法。
1、supports方法
這個接口就是看這類是用來處理哪種handler的。
2、handle方法
這個方法就是具體的處理方法,通過前面的文章的梳理,可以看到Spring在進行選擇的時候一般是通過supports方法判斷選擇哪個,然後再用handle方法去具體處理。
3、getLastModified
這個就是看這個handler最後的修改時間,,有些參數能在沒有修改的時候不進行處理。
下面我們來看下其的實作類
2、SimpleControllerHandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Controller); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } @Override public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }
這裡可以看到,這個HandlerAdapter就是通過supports看下目前要處理的handler是不是Controller。如果是,就通過handle去處理。
3、SimpleServletHandlerAdapter
public class SimpleServletHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Servlet); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; } @Override public long getLastModified(HttpServletRequest request, Object handler) { return -1; } }
這個HandlerAdapter supports方法就是判斷handler是不是Servlet,如果是就用handle去處理,直接調用Servlet的service方法。傳回的為null。
4、HttpRequestHandlerAdapter
public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; } @Override public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }
可以看到這個Adapter是用來處理HttpRequestHandler的,handle調用的是其的handleRequest方法。這個HttpRequestHandler我的了解其實這個與Servlet是類似的(因為其入參一樣是HttpServletRequest 、HttpServletResponse ),你可以自定義處理器去實作HttpRequestHandler這個接口。
SpringMVC對HttpRequestHandler的實作一般會用到的應該是兩個
1、DefaultServletHttpRequestHandler
public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware { ............ @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; if (!StringUtils.hasText(this.defaultServletName)) { if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) { this.defaultServletName = COMMON_DEFAULT_SERVLET_NAME; } else if (this.servletContext.getNamedDispatcher(GAE_DEFAULT_SERVLET_NAME) != null) { this.defaultServletName = GAE_DEFAULT_SERVLET_NAME; } else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) { this.defaultServletName = RESIN_DEFAULT_SERVLET_NAME; } else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) { this.defaultServletName = WEBLOGIC_DEFAULT_SERVLET_NAME; } else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) { this.defaultServletName = WEBSPHERE_DEFAULT_SERVLET_NAME; } else { throw new IllegalStateException("Unable to locate the default servlet for serving static content. " + "Please set the 'defaultServletName' property explicitly."); } } } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Assert.state(this.servletContext != null, "No ServletContext set"); RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName); if (rd == null) { throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + this.defaultServletName + "'"); } rd.forward(request, response); } }
這裡有些tomcat的内容,這裡先挖一個坑。
可以看到其是先通過setServletContext方法,看是哪種getNamedDispatcher,再将defaultServletName進行對應的設定,然後在通過handleRequest方法,擷取到RequestDispatcher(tomcat)的類,再調用其的forward方法。
2、ResourceHttpRequestHandler
這個類看名稱就是知道其是用來處理Resource的,這裡簡答看下其的handleRequest方法:
@Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Resource resource = getResource(request); ............ MediaType mediaType = getMediaType(request, resource); ........ ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response); if (request.getHeader(HttpHeaders.RANGE) == null) { setHeaders(response, resource, mediaType); this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage); } else { response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request); try { ........... this.resourceRegionHttpMessageConverter.write( HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage); } catch (IllegalArgumentException ex) { response.setHeader("Content-Range", "bytes */" + resource.contentLength()); response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); } } }
可以看到,這裡分為5個部分:
1、通過request,擷取對應的Resource。
2、擷取對應的MediaType。
3、通過response建構outputMessage。
4、設定response的頭部。
5、通過this.resourceRegionHttpMessageConverter.write将resource的内容寫到response中。
1、我們再通過一個demo來更直接的了解:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="beanNameUrlHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> </bean> <bean id="resourceHandler1" name="/ResourceHandlerHello.html" class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler"> <property name="locations"> <list> <ref bean="myClassPathResource"/> </list> </property> </bean> <bean id="myClassPathResource" class="org.springframework.core.io.ClassPathResource"> <constructor-arg name="path" value="static/"/> </bean> </beans>
然後在maven項目的resource目錄建立static目錄,建立ResourceHandlerHello.html靜态界面
<!DOCTYPE html> <html > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> Hello ResourceHttpRequestHandler </body> </html>
啟動tomcat項目,通路http://localhost:8080/ResourceHandlerHello.html 位址就能傳回界面:
下面 我們來debug看下流程,前面擷取到handler(resourceHandler1)我們就不再梳理了,可以看以前關于HandlerMapping的内容。這裡會根據這個bean标簽的name="/ResourceHandlerHello.html"将其注冊到BeanNameUrlHandlerMapping中,然後在請求的時候就會将對應的ResourceHttpRequestHandler傳回,然後會根據這個ResourceHttpRequestHandler(HttpRequestHandler)擷取到對應的HttpRequestHandlerAdapter,我們現在來看下這個handler的handleRequest方法:
1、先是getResource
然後這裡的getLocations方法就是我們添加的 <ref bean="myClassPathResource"/>:
可以看到這裡之後就擷取到這個resource了,擷取resource後,将将其寫到response中,這個可以看前面關于ResourceHttpRequestHandler的handleRequest方法五部介紹。
5、RequestMappingHandlerAdapter
現在就來到了算是所有HandlerAdapter中很複雜的的RequestMappingHandlerAdapter了(這也是在梳理HandlerAdapter之前,梳理了一些字首内容)
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
這章我們直接來開下其的一些主要内容,是以就不具體的介紹其的父類、及其他的一些内容了(有一些主要方法來展開這些點)
1、方法
1、構造函數(RequestMappingHandlerAdapter)
public RequestMappingHandlerAdapter() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList<>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); this.messageConverters.add(new SourceHttpMessageConverter<>()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); } private List<HttpMessageConverter<?>> messageConverters;
可以看到RequestMappingHandlerAdapter的構造函數就是設定HttpMessageConverter,這個HttpMessageConverter前面梳理HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler接口的時候有了解,一般是用與實作AbstractMessageConverterMethodArgumentResolver抽象類中,用來操作request body/response body的。
2、getDefaultArgumentResolvers(RequestMappingHandlerAdapter)
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); .......... resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); .......... resolvers.add(new ModelMethodProcessor()); ............. return resolvers; }
可以看到這裡在建構RequestResponseBodyMethodProcessor(HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler實作類)的時候,會傳入前面設定的預設HttpMessageConverter。
3、afterPropertiesSet(RequestMappingHandlerAdapter)
這個是因為其實作了InitializingBean接口
@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); } }
可以看到這裡是有建立argumentResolvers(HandlerMethodArgumentResolverComposite)、returnValueHandlers(HandlerMethodReturnValueHandlerComposite),将前面的那些resolvers(HandlerMethodArgumentResolver)、handlers(HandlerMethodReturnValueHandler)傳入完成其的初始化。
4、initControllerAdviceCache
這個方法是上面afterPropertiesSet調用的
private void initControllerAdviceCache() { .............. List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); AnnotationAwareOrderComparator.sort(adviceBeans); List<Object> requestResponseBodyAdviceBeans = new ArrayList<>(); for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); ....... Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(adviceBean, attrMethods); } Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); .......... // this.initBinderAdviceCache.put(adviceBean, binderMethods); if (RequestBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } } ......... } public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) { List<ControllerAdviceBean> beans = new ArrayList<>(); for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) { if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) { beans.add(new ControllerAdviceBean(name, applicationContext)); } } return beans; }
這裡就是從BeanFactory中擷取所有bean(Object),然後判斷該bean有沒有注解ControllerAdvice,如果是就産生ControllerAdviceBean添加到beans中,然後再周遊。
這裡有兩個MethodFilter
public static final MethodFilter INIT_BINDER_METHODS = method -> AnnotationUtils.findAnnotation(method, InitBinder.class) != null; public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method -> ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
如果bean的方法有InitBinder、RequestMapping這些注解,就将其添加到Cache中,下面同樣是判斷RequestBodyAdvice、ResponseBodyAdvice。
5、handleInternal
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { ........這裡是與response中有些cache設定有關 } return mav; } private boolean synchronizeOnSession = false;
這裡就是整個RequestMappingHandlerAdapter的的主要方法handleInternal
首先是checkRequest,可以看到這裡是對method 、Session的一些判斷。requireSession 預設為false,如果主動修改為true,就會校驗
protected final void checkRequest(HttpServletRequest request) throws ServletException { // Check whether we should support the request method. String method = request.getMethod(); if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods); } if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } } private boolean requireSession = false;
然後是看需不需要使用同步,調用invokeHandlerMethod方法。
6、invokeHandlerMethod
@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); 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); ......... if (asyncManager.hasConcurrentResult()) { .......... } invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } } public boolean hasConcurrentResult() { return (this.concurrentResult != RESULT_NONE); } private Object concurrentResult = RESULT_NONE;
這篇文章之前的那些前置知識點,主要就是用在這裡了。
7、getDataBinderFactory
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>(); this.initBinderAdviceCache.forEach((clazz, methodSet) -> { if (clazz.isApplicableToBeanType(handlerType)) { Object bean = clazz.resolveBean(); for (Method method : methodSet) { initBinderMethods.add(createInitBinderMethod(bean, method)); } } }); for (Method method : methods) { Object bean = handlerMethod.getBean(); initBinderMethods.add(createInitBinderMethod(bean, method)); } return createDataBinderFactory(initBinderMethods); }
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods) throws Exception { return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()); }
這裡主要是就是将@InitBinder注解的的方法,不管是前面的ControllerAdvice注解添加的cache中的(initBinderAdviceCache)),還是目前handlerType本身有這個注解,都建立對應的InvocableHandlerMethod對象,通過createInitBinderMethod方法。
8、createInitBinderMethod
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) { InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method); if (this.initBinderArgumentResolvers != null) { binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers); } binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer)); binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); return binderMethod; }
這裡就是建立binderFactory的内容(ServletRequestDataBinderFactory)。
9、getModelFactory
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.modelAttributeCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); this.modelAttributeCache.put(handlerType, methods); } List<InvocableHandlerMethod> attrMethods = new ArrayList<>(); // Global methods first this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> { if (clazz.isApplicableToBeanType(handlerType)) { Object bean = clazz.resolveBean(); for (Method method : methodSet) { attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } } }); for (Method method : methods) { Object bean = handlerMethod.getBean(); attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); } public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods, WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) { if (handlerMethods != null) { for (InvocableHandlerMethod handlerMethod : handlerMethods) { this.modelMethods.add(new ModelMethod(handlerMethod)); } } this.dataBinderFactory = binderFactory; this.sessionAttributesHandler = attributeHandler; }
這裡與上面WebDataBinderFactory 類似,不過這裡是使用就是MODEL_ATTRIBUTE_METHODS這個MethodFilter。例如目前handlerMethod對應handlerType對應的@RequestMapping、@ModelAttribute注解(這個前置知識裡面有分析),然後去建立ModelFactory。
10、createModelAttributeMethod
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) { InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method); if (this.argumentResolvers != null) { attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); attrMethod.setDataBinderFactory(factory); return attrMethod; }
這裡也是将這些method建立為InvocableHandlerMethod。我們再回到前面的invokeHandlerMethod方法。
11、createInvocableHandlerMethod
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) { return new ServletInvocableHandlerMethod(handlerMethod); }
這裡是将目前要處理的HandlerMethod轉化為ServletInvocableHandlerMethod。再之後就對其進行一些初始化設定argumentResolvers、returnValueHandlers。
12、modelFactory.initModel
這些都處理後之後就是initModel
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); 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); } } }
這裡主要是invokeModelAttributeMethods,去執行modelMethods
13、invokeModelAttributeMethods
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; } Object returnValue = modelMethod.invokeForRequest(request, container); if (!modelMethod.isVoid()){ String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType()); if (!ann.binding()) { container.setBindingDisabled(returnValueName); } if (!container.containsAttribute(returnValueName)) { container.addAttribute(returnValueName, returnValue); } } } } boolean binding() default true; public boolean containsAttribute(String name) { return getModel().containsAttribute(name); }
要更好的了解整個地方,可以去看下前面寫的這篇文章的demo部分,這裡就不多贅叙了。這裡再加一種情況來說明整個情況,就是有傳回的:
@ModelAttribute public StudentVo modelAttributeMethodResult(@RequestParam("age") Integer age , @RequestParam("type") Short type, Model model) { StudentVo studentVo = new StudentVo(); studentVo.setAge(age); return studentVo; }
這裡如果主動設定binding為false,如果目前ModelAttribute注解的name在container的model中有,将其設定到container的Model中,直接return。如果還沒有,就進行下面的invokeForRequest方法直接執行該方法。
我們想看前一篇的demo在這裡的設定:
這裡是傳回void,是以沒有下面的操作。下面來看執行的第二個方法,有傳回的:
擷取預設名稱,如果沒有,将其添加的model中,供以後去擷取
這裡的initModel方法完成後,就是AsyncWebRequest,這個是與異步相關的,SpringMVC不會主動去處理,這裡就先跳過。
14、invocableMethod.invokeAndHandle
這個方法也在前面文章的前置知識點有單獨提取處理,這個就是去執行MethodHandler的,這裡就不再過多贅述。
15、getModelAndView
執行完MethodHandler後,就是去擷取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()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }
這裡主要是兩個點,一個isRequestHandled,這個我們再以前梳理HandlerMapping的時候有設定,現在可以知道這個設定是幹什麼的了,就是表示不需要再往下,去處理與View相關的内容了(簡單來說就是不需要使用ModelAndView )。第二個就是通過mavContainer去擷取建立ModelAndView。
總結
至此,關于HandlerAdapter的的父子結構與源碼梳理就完成了。