这一章我们主要是梳理下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 lang="en"> <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的的父子结构与源码梳理就完成了。