diff --git a/README.md b/README.md index a4a8d0c..201b9bd 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ -## 源码【06.21更新】 +## 源码 @@ -151,6 +151,8 @@ [09.第9章-SpringMVC请求处理源码和HandlerMapping原理](docs/spring-sourcecode-v1/09.第9章-SpringMVC请求处理源码和HandlerMapping原理.md) +[10.第10章-SpringMVC中的HandlerAdapter源码解析](docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md) + # Netty ## 入门 diff --git a/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md b/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md new file mode 100644 index 0000000..2bb655c --- /dev/null +++ b/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md @@ -0,0 +1,1358 @@ +# 第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); + } +``` + + + +由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。 + + + + + + + + +