# 第10章-SpringMVC中的HandlerAdapter源码解析 ## 测试类 ### HelloController ```java 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 ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> $Title$ $END$ 结果:

${sessionScope.msg}

``` 其余的类和前面一样,不列举了。 ## BeanNameUrlHandlerMapping简介 ```java public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { /** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override //决定使用哪个url注册 protected String[] determineUrlsForHandler(String beanName) { List 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); } } ``` 1. 很简单,意思就是只要你的BeanName是以/开头,就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。 2. 具体路径怎么写呢?你可以在你的类上写一个`@Controller("/helloReq")` ,这样就有路径了。代表的意思就是`/helloReq`这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里 3. 如果不懂的话可以待会看下面的例子 > handler就是咱们常说的XXXController ## HandlerAdapter概述 1. 上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法 2. 按照我们最简单的想法就是直接method.invoke()反射执行 3. 但是实际上我们要考虑 1. 是哪个对象执行方法? 2. 方法里的参数有几个? 3. 参数类型是什么?参数类型大概率有我们自己写的类,怎么处理 4. 多个参数我们还要一个一个赋值。 5. 怎么返回?是直接返回值,还是跳转页面,等等 4. 其实是比较复杂的,springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑 还是跟HandlerMapping一样,继续Debug `DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)` 浏览器输入:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&user=haha开始测试 ### DispatcherServlet#doDispatch()请求派发 ```java //SpringMVC处理请求的核心流程 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链 boolean multipartRequestParsed = false; //文件上传标志 //对异步请求的支持(Servlet3.0以后才有的,Webflux) WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); //检查当前是否文件上传请求 multipartRequestParsed = (processedRequest != request); //构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { //如果找不到人处理,就send 404 noHandlerFound(processedRequest, response); return; } //适配器怎么找的、 Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. 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; } } //所有拦截器的 preHandle 执行 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; //使用 mappedHandler整个链 } //真正来执行目标方法 Actually invoke the handler. 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) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. 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()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } } ``` F7进入`getHandlerAdapter(mappedHandler.getHandler())` ### DispatcherServlet#getHandlerAdapter() 上面几个默认的适配器还是在`DispatcherServlet.properties`配置的4个默认适配器,然后在初始化九大组件的时候放到容器里 ### HttpRequestHandlerAdapter#supports() ```java @Override //想让他工作,实现HttpRequestHandler接口即可 public boolean supports(Object handler) { //写一个HttpRequestHandler的实现也能处理请求 return (handler instanceof HttpRequestHandler); } ``` 这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了 ### SimpleControllerHandlerAdapter#supports() ```java @Override public boolean supports(Object handler) { return (handler instanceof Controller); } ``` 同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了 ### RequestMappingHandlerAdapter RequestMappingHandlerAdapter自己没有写`supports`方法,它用的是父类的AbstractHandlerMethodAdapter的 #### AbstractHandlerMethodAdapter#supports() ```java public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); } ``` 这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod #### RequestMappingHandlerAdapter#supportsInternal() ```java protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; } ``` ### DispatcherServlet#doDispatch() ## 举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动 ### 测试类-HelloHttpRequestHandler ```java @Controller("/helloReq") //BeanNameUrlHandlerMapping 就会把他注册进去 public class HelloHttpRequestHandler implements HttpRequestHandler { //启用 HttpRequestHandlerAdapter //处理请求 @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 ```java //@RequestMapping("/ssss") @Controller("/helloSimple") public class HelloSimpleController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { return null; } } ``` 实现Controller接口的就是SimpleControllerHandlerAdapter来处理,原理和HttpRequestHandlerAdapter几乎一样。 ### 小总结 1. 很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合,一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦,不像@RequestMapping那么方便 2. 远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后,一个类里可以写多个@RequestMapping注解标注的方法,一个类里就可以处理多个URL请求,并且处理参数和返回值都很方便。 3. 所以下面就重点讲RequestMappingHandlerAdapter ## RequestMappingHandlerAdapter中的参数解析器、返回值处理器=>概述 ### DispatcherServlet#doDispatch() ### AbstractHandlerMethodAdapter#handle() ```java public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); } ``` AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过 ### RequestMappingHandlerAdapter#handleInternal() ```java protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // 会话锁,每一个用户和服务器交互无论发了多少请求都只有一个会话,限制用户的线程 Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { //高并发可以限制一个用户一次进来一个请求 mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { //执行目标方法 No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; } //session锁这个东西不适用于高并发场景,所以spring默认是不适用它,应该是可以开启的,具体怎么开启我没研究,可以看官方文档 private boolean synchronizeOnSession = false; ``` ### RequestMappingHandlerAdapter#invokeHandlerMethod( ) ```java /** * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} * if view resolution is required. * @since 4.2 * @see #createInvocableHandlerMethod(HandlerMethod) */ @Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //把原生的request,response封装到一个对象中方便后续只用这一个参数就行【装饰器模式】 ServletWebRequest webRequest = new ServletWebRequest(request, response); try { //数据绑定器 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); //获取到模型工厂 Model(要交给页面的数据) View(我们要去的 视图),视图可以理解为页面或者图片等等 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 这里就是做了一些增强,比如我们可以直接通过ServletInvocableHandlerMethod拿到返回值,可以很方便的拿到其它我们需要的信息 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是以后处理过程中产生的ModelAndView数据临时存储容器 // 也是整个请求处理流程,线程共享数据 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? ```java public String sayHello(Person person){ //... } ``` 1. 假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的 2. 前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。 #### argumentResolvers参数解析器 注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器 #### returnValueHandlers返回值解析器 ### 15个参数解析器和27个返回值解析器是什么时候有值的? ```java public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { // ... } ``` 我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口,所以它什么时候有值,和RequestMappingHandlerMapping是一样的逻辑 1. `DispatcherServlet#onRefresh()`开始初始化九大组件,就会开始初始化HandlerAdapter 2. 首先是创建`DispatcherServlet.properties`里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的 3. 其中RequestMappingHandlerAdapter创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerAdapter#afterPropertiesSet() 4. 在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器 #### RequestMappingHandlerAdapter#afterPropertiesSet() ```java public void afterPropertiesSet() { //初始化以后 // Do this first, it may add ResponseBody advice beans initControllerAdviceCache(); //初始化 ControllerAdvice 【异常处理相关的功能】 if (this.argumentResolvers == null) { //拿到底层所有的默认 argumentResolvers List resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); //把这些resolver统一组合到一个对象里面,方便管控 } if (this.initBinderArgumentResolvers == null) { List resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } } ``` #### RequestMappingHandlerAdapter#getDefaultArgumentResolvers() ```java private List getDefaultArgumentResolvers() { List resolvers = new ArrayList<>(30); // Annotation-based argument resolution //这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲 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()); // Type-based argument resolution 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()); } // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new PrincipalMethodArgumentResolver()); //这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; } ``` #### RequestMappingHandlerAdapter#getDefaultReturnValueHandlers() ```java private List getDefaultReturnValueHandlers() { List handlers = new ArrayList<>(20); // Single-purpose return value types 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)); // Annotation-based return value types handlers.add(new ServletModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all 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()真正开始执行目标方法 ```java 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( )目标方法的反射执行过程 ```java 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()获取方法的请求参数 ```java protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //直接拿到方法的所有参数,getMethodParameters是HandlerMethod类的,在之前构造handler的时候就已经通过反射将相关信息保存好了 // 我们之前讲过标注@RequestMapping注解的handler最终会被封装成HandlerMethod MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } //准备args数组(和parameters一样长),挨个确定每个参数都是什么值 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)) { //支持这种参数的解析器也会被放到缓存,下一次进来,就不用27个人挨个判断 throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... 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() ```java public boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null; } ``` ### HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数 ```java 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注解的参数解析器 ```java package org.springframework.web.method.annotation public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(RequestParam.class)) { //注意这里是为了不跟下面的RequestParamMapMethodArgumentResolver产生冲突 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包下的 1. 第一次进入`RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)`时,我们在前面讲过,在`RequestMappingHandlerAdapter#getDefaultArgumentResolvers()`添加默认解析器的时候,spring往参数解析器里加了两个 ```java //这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); //这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); ``` 2. 这里的true和false指定的就是useDefaultResolution这个东西 3. 然后我们验证一下第二个RequestParamMethodArgumentResolver 4. useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个`RequestParamMethodArgumentResolver`(双重默认) 5. 那什么时候用第27个`ServletModelAttributeMethodProcessor`这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。 ### RequestParamMapMethodArgumentResolver参数解析器 > `@RequestParam Map params` ```java 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注解的参数解析器 ```java 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到底能写哪些参数? 详见[官方文档-参数相关](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments) ## RequestMappingHandlerAdapter中的返回值解析器工作流程 ```java @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) { //@RequestParam Map params:所有请求参数全封装进来 // @RequestHeader("User-Agent") String ua 获取指定请求头的值 String header = request.getHeader("User-Agent"); //方法的签名,到底能写那些? //详细参照 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments //https://www.bilibili.com/video/BV19K4y1L7MT?p=32 String mvc = helloService.say(user + ":MVC" + name); session.setAttribute("msg", mvc); //SpringMVC的目标方法能写哪些返回值 //https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types return "index.jsp"; } } ``` ### ServletInvocableHandlerMethod#invokeAndHandle() 我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲 ### HandlerMethodReturnValueHandlerComposite#handleReturnValue() ```java @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个返回值处理器找到合适的返回值处理器 ```java private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); //还是老样子for循环 for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; } ``` 我们就直接放行,看最终找到的是哪个 ### ViewNameMethodReturnValueHandler#supportsReturnType() ```java public boolean supportsReturnType(MethodParameter returnType) { Class paramType = returnType.getParameterType(); //返回值是void,或者字符串 return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType)); } ``` ### 返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue() ### ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值 ```java 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)) { //是否是重定向的方式 redirect: mavContainer.setRedirectModelScenario(true); } } else if (returnValue != null) { // should not happen throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } ``` 然后就准备返回了 ### 返回到RequestMappingHandlerAdapter#invokeHandlerMethod() ### RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作 ```java private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { //modelFactory准备模型数据 (请求域数据共享)session里面的数据搬家到request域 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 flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { //重定向数据的共享,RedirectView。先把数据移到request,再把request移到session RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav; } ``` 注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。 ### 返回到DispatcherServlet#doDispatch() ### DispatcherServlet#applyDefaultViewName() ```java 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()处理返回值 ```java 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();//即使有异常,这里也会返回ModelAndView } else { //定义无数种异常解析器就会得到不同的异常解析效果 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // 动态策略。 Did the handler return a view to render? 为啥?@ResponseBody(提前在解析返回值的时候,就已经把数据写出去了,所以这一步就没有了) if (mv != null && !mv.wasCleared()) { render(mv, request, response); //渲染ModeAndView,来解析模型和视图;最终决定响应效果 if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); } } ``` 进入此方法 ### DispatcherServlet#render()渲染ModelAndView ```java protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null) { // We need to resolve the view name. 关键还是这里 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 { // No need to lookup: the ModelAndView object contains the actual View object. 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() + "'"); } } // Delegate to the View object for rendering. 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() ```java protected View resolveViewName(String viewName, @Nullable Map model, Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) { //一样的for循环视图解析器 for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; } ``` 不过默认的视图解析器只有一个 这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html ### RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器 ```java @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 //如果返回值标注了 @ResponseBody注解。会被这个人拦截处理器 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); // Try even with null return value. ResponseBodyAdvice could get involved. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); } ``` 由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。