第10章-SpringMVC中的HandlerAdapter源码解析 测试类 HelloController package cn.imlql.web.controller;@Controller public class HelloController { public HelloController () { System.out.println("HelloController....." ); } @Autowired HelloService helloService; @GetMapping("/hello") public String sayHello (String name, @RequestParam("user") String user, HttpSession session) { String mvc = helloService.say(user + ":MVC" + name); session.setAttribute("msg" , mvc); return "index.jsp" ; } }
index.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> $END$ 结果: <h1 style="color: red">${sessionScope.msg}</h1> </body> </html>
其余的类和前面一样,不列举了。
BeanNameUrlHandlerMapping简介 public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<>(); if (beanName.startsWith("/" )) { urls.add(beanName); } String[] aliases = obtainApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/" )) { urls.add(alias); } } return StringUtils.toStringArray(urls); } }
很简单,意思就是只要你的BeanName是以/开头,就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。
具体路径怎么写呢?你可以在你的类上写一个@Controller("/helloReq")
,这样就有路径了。代表的意思就是/helloReq
这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里
如果不懂的话可以待会看下面的例子
handler就是咱们常说的XXXController
HandlerAdapter概述
上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法
按照我们最简单的想法就是直接method.invoke()反射执行
但是实际上我们要考虑
是哪个对象执行方法?
方法里的参数有几个?
参数类型是什么?参数类型大概率有我们自己写的类,怎么处理
多个参数我们还要一个一个赋值。
怎么返回?是直接返回值,还是跳转页面,等等
其实是比较复杂的,springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑
还是跟HandlerMapping一样,继续Debug DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)
浏览器输入:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&user=haha开始测试
DispatcherServlet#doDispatch()请求派发
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; } HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET" .equals(method); if (isGet || "HEAD" .equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return ; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return ; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed" , err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed" , err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null ) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
F7进入getHandlerAdapter(mappedHandler.getHandler())
DispatcherServlet#getHandlerAdapter()
上面几个默认的适配器还是在DispatcherServlet.properties
配置的4个默认适配器,然后在初始化九大组件的时候放到容器里
HttpRequestHandlerAdapter#supports() @Override public boolean supports (Object handler) { return (handler instanceof HttpRequestHandler); }
这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了
SimpleControllerHandlerAdapter#supports() @Override public boolean supports (Object handler) { return (handler instanceof Controller); }
同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了
RequestMappingHandlerAdapter RequestMappingHandlerAdapter自己没有写supports
方法,它用的是父类的AbstractHandlerMethodAdapter的
AbstractHandlerMethodAdapter#supports() public final boolean supports (Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod
RequestMappingHandlerAdapter#supportsInternal() protected boolean supportsInternal (HandlerMethod handlerMethod) { return true ; }
DispatcherServlet#doDispatch()
举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动 测试类-HelloHttpRequestHandler @Controller("/helloReq") public class HelloHttpRequestHandler implements HttpRequestHandler { @Override public void handleRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("HelloHttpRequestHandler...." ); } }
@Controller("/helloReq")
里的/helloReq即是BeanName也是URL。这里就是之前说的BeanName以/为开头的Bean,它的URL和handler的映射关系会被保存在BeanNameUrlHandlerMapping里,如下图
DispatcherServlet#getHandlerAdapter()
因为我们的HelloHttpRequestHandler正好实现了HttpRequestHandler,所以就会直接返回HttpRequestHandlerAdapter
DispatcherServlet#doDispatch() 我们直接来到准备执行的地方
F7进入ha.handle(processedRequest, response, mappedHandler.getHandler())
HttpRequestHandlerAdapter#handle()
直接来到了这里,接着就调用我们自定义的HelloHttpRequestHandler的handleRequest方法。
测试类-HelloSimpleController @Controller("/helloSimple") public class HelloSimpleController implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response) throws Exception { return null ; } }
实现Controller接口的就是SimpleControllerHandlerAdapter来处理,原理和HttpRequestHandlerAdapter几乎一样。
小总结
很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合,一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦,不像@RequestMapping那么方便
远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后,一个类里可以写多个@RequestMapping注解标注的方法,一个类里就可以处理多个URL请求,并且处理参数和返回值都很方便。
所以下面就重点讲RequestMappingHandlerAdapter
RequestMappingHandlerAdapter中的参数解析器、返回值处理器=>概述 DispatcherServlet#doDispatch()
AbstractHandlerMethodAdapter#handle() public final ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过
RequestMappingHandlerAdapter#handleInternal() 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)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this .cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; } private boolean synchronizeOnSession = false ;
RequestMappingHandlerAdapter#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); 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(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]" ; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null ; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
什么是WebDataBinderFactory? public String sayHello (Person person) { }
假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的
前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。
argumentResolvers参数解析器
注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器
returnValueHandlers返回值解析器
15个参数解析器和27个返回值解析器是什么时候有值的? public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware , InitializingBean { }
我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口,所以它什么时候有值,和RequestMappingHandlerMapping是一样的逻辑
DispatcherServlet#onRefresh()
开始初始化九大组件,就会开始初始化HandlerAdapter
首先是创建DispatcherServlet.properties
里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的
其中RequestMappingHandlerAdapter创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerAdapter#afterPropertiesSet()
在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器
RequestMappingHandlerAdapter#afterPropertiesSet() public void afterPropertiesSet () { 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); } }
RequestMappingHandlerAdapter#getDefaultArgumentResolvers() private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers () { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30 ); resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false )); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false )); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this .requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this .requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this .requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); if (KotlinDetector.isKotlinPresent()) { resolvers.add(new ContinuationHandlerMethodArgumentResolver()); } if (getCustomArgumentResolvers() != null ) { resolvers.addAll(getCustomArgumentResolvers()); } resolvers.add(new PrincipalMethodArgumentResolver()); resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true )); resolvers.add(new ServletModelAttributeMethodProcessor(true )); return resolvers; }
RequestMappingHandlerAdapter#getDefaultReturnValueHandlers() private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers () { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20 ); handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this .reactiveAdapterRegistry, this .taskExecutor, this .contentNegotiationManager)); handlers.add(new StreamingResponseBodyReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this .contentNegotiationManager, this .requestResponseBodyAdvice)); handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this .beanFactory)); handlers.add(new ServletModelAttributeMethodProcessor(false )); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this .contentNegotiationManager, this .requestResponseBodyAdvice)); handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); if (getCustomReturnValueHandlers() != null ) { handlers.addAll(getCustomReturnValueHandlers()); } if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); } else { handlers.add(new ServletModelAttributeMethodProcessor(true )); } return handlers; }
RequestMappingHandlerAdapter中的参数解析器工作流程 DispatcherServlet#doDispatch()
AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )
和RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )
上面说过,跳过
RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法
ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法 public void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null ) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true ); return ; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true ); return ; } mavContainer.setRequestHandled(false ); Assert.state(this .returnValueHandlers != null , "No return value handlers" ); try { this .returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程 public Object invokeForRequest (NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } return doInvoke(args); }
开始进入正题
InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0 ; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this .parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null ) { continue ; } if (!this .resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver" )); } try { args[i] = this .resolvers.resolveArgument(parameter, mavContainer, request, this .dataBinderFactory); } catch (Exception ex) { if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }
HandlerMethodArgumentResolverComposite准备循环27个参数解析器
HandlerMethodArgumentResolverComposite#supportsParameter() public boolean supportsParameter (MethodParameter parameter) { return getArgumentResolver(parameter) != null ; }
HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数 private HandlerMethodArgumentResolver getArgumentResolver (MethodParameter parameter) { HandlerMethodArgumentResolver result = this .argumentResolverCache.get(parameter); if (result == null ) { for (HandlerMethodArgumentResolver resolver : this .argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this .argumentResolverCache.put(parameter, result); break ; } } } return result; }
这里就是循环27个参数解析器,看哪一个能解析这种类型的参数。因为参数解析器太多,我们就举几个常用的例子
RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器 package org.springframework.web.method.annotation public boolean supportsParameter (MethodParameter parameter) { if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); } else { return true ; } } else { if (parameter.hasParameterAnnotation(RequestPart.class)) { return false ; } parameter = parameter.nestedIfOptional(); if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { return true ; } else if (this .useDefaultResolution) { return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); } else { return false ; } } }
这个就是判断参数有没有标@RequestParam注解
注意这个是org.springframework.web.method.annotation包下的
第一次进入RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)
时,我们在前面讲过,在RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
添加默认解析器的时候,spring往参数解析器里加了两个
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false )); resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true ));
这里的true和false指定的就是useDefaultResolution这个东西
然后我们验证一下第二个RequestParamMethodArgumentResolver
useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个RequestParamMethodArgumentResolver
(双重默认)
那什么时候用第27个ServletModelAttributeMethodProcessor
这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。
RequestParamMapMethodArgumentResolver参数解析器
@RequestParam Map<String,Object> params
public boolean supportsParameter (MethodParameter parameter) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name())); }
判断有没有标@RequestParam注解,并且类型是Map
PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器 public boolean supportsParameter (MethodParameter parameter) { if (!parameter.hasParameterAnnotation(PathVariable.class)) { return false ; } if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class); return (pathVariable != null && StringUtils.hasText(pathVariable.value())); } return true ; }
相信大家已经能猜到了,这里就是判断参数上有没有标注@PathVariable注解
返回到InvocableHandlerMethod#getMethodArgumentValues( )
具体的参数赋值过程,就是一些数据类型的转换,可以自己去看下。
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
返回到InvocableHandlerMethod#invokeForRequest()
接下来就是交给反射去执行
返回到ServletInvocableHandlerMethod#invokeAndHandle()
拿到返回值了,接下来交给返回值处理器
SpringMVC到底能写哪些参数? 详见官方文档-参数相关
RequestMappingHandlerAdapter中的返回值解析器工作流程 @Controller public class HelloController { public HelloController () { System.out.println("HelloController....." ); } @Autowired HelloService helloService; @GetMapping("/hello") public String sayHello (String name, @RequestParam("user") String user, HttpSession session, HttpServletRequest request, //原生的session对象 @RequestHeader("User-Agent") String ua) { String header = request.getHeader("User-Agent" ); String mvc = helloService.say(user + ":MVC" + name); session.setAttribute("msg" , mvc); return "index.jsp" ; } }
ServletInvocableHandlerMethod#invokeAndHandle()
我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲
HandlerMethodReturnValueHandlerComposite#handleReturnValue() @Override public void handleReturnValue (@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null ) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器 private HandlerMethodReturnValueHandler selectHandler (@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this .returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue ; } if (handler.supportsReturnType(returnType)) { return handler; } } return null ; }
我们就直接放行,看最终找到的是哪个
ViewNameMethodReturnValueHandler#supportsReturnType() public boolean supportsReturnType (MethodParameter returnType) { Class<?> paramType = returnType.getParameterType(); return (void .class == paramType || CharSequence.class.isAssignableFrom(paramType)); }
返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()
ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值 public void handleReturnValue (@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue instanceof CharSequence) { String viewName = returnValue.toString(); mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { mavContainer.setRedirectModelScenario(true ); } } else if (returnValue != null ) { throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } }
然后就准备返回了
返回到RequestMappingHandlerAdapter#invokeHandlerMethod()
RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作 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, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null ) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav; }
注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。
返回到DispatcherServlet#doDispatch()
DispatcherServlet#applyDefaultViewName() private void applyDefaultViewName (HttpServletRequest request, @Nullable ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null ) { mv.setViewName(defaultViewName); } } } protected String getDefaultViewName (HttpServletRequest request) throws Exception { return (this .viewNameTranslator != null ? this .viewNameTranslator.getViewName(request) : null ); } @Nullable private RequestToViewNameTranslator viewNameTranslator;
这里不是重点,我直接告诉你结果吧,默认页面就是把request的请求路径直接拿来当要去的页面地址 。
比如你的请求路径是@GetMapping("/hello.html")
,但是你返回值写的是void,那么它就会给你返回到hello.html页面
返回到DispatcherServlet#doDispatch()
来到拦截器的后置处理环节
然后来到处理结果环节
springmvc能写哪些返回值 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
视图解析器解析流程 DispatcherServlet#processDispatchResult()处理返回值 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned." ); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return ; } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, null ); } }
进入此方法
DispatcherServlet#render()渲染ModelAndView protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = (this .localeResolver != null ? this .localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null ) { view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null ) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'" ); } } else { view = mv.getView(); if (view == null ) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'" ); } } if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] " ); } try { if (mv.getStatus() != null ) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]" , ex); } throw ex; } }
DispatcherServlet#resolveViewName() protected View resolveViewName (String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { if (this .viewResolvers != null ) { for (ViewResolver viewResolver : this .viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null ) { return view; } } } return null ; }
不过默认的视图解析器只有一个
这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html
RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器 @Override public boolean supportsParameter (MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } @Override public boolean supportsReturnType (MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } public Object resolveArgument (MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); if (binderFactory != null ) { WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null ) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } if (mavContainer != null ) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return adaptArgumentIfNecessary(arg, parameter); } @Override public void handleReturnValue (@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true ); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。