Spring MVC 源码笔记 关键类分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 WebMvcConfigurationSupport 默认注册了很多东西,如 HandlerMapping 几个实现, HandlerAdaptor 几个实现 HandlerMapping 添加容器内所有带有 RequestMaping 的类的公开方法到 mappings 中存起来 (AbstractHandlerMethodMapping 根据 request 的 uri 查找对应的 HandlerMethod, 步骤概述: 把RequestMapping注解内的 path 作为 key 保持到一个 map1 其他信息封装成 mapping 作为 key 也保持到另一个 map2 根据 uri去 map1 获取 mapping, 再根据 mapping 获取 HandlerMethod 封装成 Match 对象, 与其他匹配对象做比较后, 返回 HandlerMethod HandlerAdapter 初始化参数解析,返回值解析等 (RequestMappingHandlerAdapter 根据 handler 确定对应的 HandlerAdapter, 然后 HandlerAdapter 负责执行这个 handler 如 RequestMappingHandlerAdapter 则负责执行 HandlerMethod 简单说就是封装 HandlerMethod, 根据参数值设置参数, 然后调用方法, 再处理返回值封装成 ModelAndView 另外,这里如果使用了@ResponseBody,会进入 RequestResponseBodyMethodProcessor 然后使用 messageConverters(json)写入到响应流 最后 mv 也直接返回null, 不需要render了. ViewResolver 负责将 ModelAndView 解析成HTML, 如JSP, FreeMarker HandlerExecutionChian 管理拦截器和封装Handler, 负责拦截器的实际调用逻辑实现 DispatcherServlet 调度整个HTTP请求响应流程, 调用各个子组件负责执行处理方法, 解析视图, 处理异常等.
SSM项目 Spring 容器和 Spring MVC 容器的创建过程及关系 Spring 容器的创建 1 2 3 4 5 6 7 8 org.springframework.web.context.ContextLoader 1.先判断此方法是否重复运行, 若重复则报异常. 2.记录日志, 记录开始时间用于计算启动消耗时间 3.将容器对象存储在本地而非 servletContext, 防止 ServletContext 关闭后无法访问 4.根据配置的类名, 实例化一个容器并赋值给 this.context 5.标准代码, 但其实就是 setParent(null), 前提是不扩展子类咯. 也就是说这个就是顶级容器了 6.根据 web.xml 的配置对容器做相应的配置, 初始化, 将 ServletContext 存入到 environment 对象中; 执行 InitializerClass, 调用容器 refresh 完成容器加载 7.打印容器初始化完成日志和记录耗时
总结: 监听 ServletContext 创建完毕的事件, 然后创建一个 web 容器, 配置一些东西(id, parent, environment)并初始化(refresh)
MVC 子容器的创建 1 2 1.首先是 DispatchServlet 本身是一个 Servlet, 因此他有生命周期 init(), 其父类 init() 会将 ServletConfig 的配置(web.xml 中的 initParams)与 servlet 对象绑定, 这样 DispatchServlet 对象的字段就有值了. 2.然后在 DispatchServlet 的父类 FrameworkServlet
总结: 就是利用 Servlet 的生命周期只会执行一次 init 的特性, 查找父容器, 创建子容器.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 org.springframework.web.servlet.HttpServletBean#init org.springframework.web.servlet.FrameworkServlet#initServletBean org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext org.springframework.web.servlet.DispatcherServlet#initStrategies
DispatchServlet 的创建 1 2 3 1.Tomcat 会创建配置在 web.xml 的 Servlet 2.接着会触发 init 方法, init 调用了创建子容器的方法后, 还添加了容器加载完毕事件监听来回调 DispatcherServlet 3.DispatcherServlet
总结: 和子容器的创建息息相关.
Dispatch 过程 1 2 3 4 5 6 7 1.覆盖 HttpServlet 的 service 方法, 调用 FrameworkServlet 2.此方法中进行一些上下文的准备工作, 以及处理日志, 异常(非 controller 异常), 然后调用 DispatcherServlet 3.doService() 中对 request 做一些准备工作, 然后调用 DispatcherServlet 4.doDispatch() 先用 handlerMappings 查找合适的 handler(并加入拦截器链), 再通过 handlerAdapters 得到 handler 的适配器, 在合适的地方触发拦截器; 然后调用适配器的 handle() 得到 ModelAndView 5.得到 ModelAndView 后, 先判断是否捕获到了异常, 是则调用 handlerExceptionResolvers 的 resolveException() 处理异常 6.接着, 调用 viewResolvers 的 resolveViewName, 将 viewName 解析成一个 View 对象 7.调用 View 对象的 render(), 将视图通过 response 响应到前端.
总结: handlerMappings 找 handler 并包装拦截器链, handlerAdapters 找可执行的 HandlerAdapter, viewResolvers 解析视图, 渲染视图.
超长源码注释 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null ) { processRequest(request, response); } else { super .service(request, response); } }
接着进入到 processRequest, 我只写注释了啊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 org.springframework.web.servlet.FrameworkServlet#processRequest org.springframework.web.servlet.DispatcherServlet#doService org.springframework.web.servlet.DispatcherServlet#doDispatch org.springframework.web.servlet.DispatcherServlet#getHandler org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle org.springframework.web.servlet.DispatcherServlet#processDispatchResult org.springframework.web.servlet.DispatcherServlet#processHandlerException org.springframework.web.servlet.DispatcherServlet#render org.springframework.web.servlet.DispatcherServlet#resolveViewName
某些实现原理 1 2 3 4 5 1.拦截器原理 2.默认的 HandlerMapping/HandlerAdapter 在何时加入? 3.参数是如何与 HTTP 请求 body 绑定的(序列化, 格式化, 绑定)? 4.参数校验是如何进行的? 5.有关参数与返回值的一些拦截与干预(@RequestBody, @ResponseBody).
拦截器原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 何时加入? 从 WebMvcConfigurationSupport 的子类中调用 addInterceptors 添加一些拦截器和拦截器的路径配置 InterceptorRegistry 和 MappedInterceptor 实现拦截器路径匹配, 在 new HandlerExecutionChian 时判断 何时执行? DispatcherServlet 负责在正确的时机调用 HandlerExecutionChian 来调用 preHanlde 等方法. 拿到 HandlerExecutionChian 后调用 preHanlde HandlerAdapter 执行完 handler 后, 调用 postHandle 解析视图并渲染到response之后, 调用 afterCompletion 如果中途出现异常, 或 preHandle 提前结束, 则也调用 afterCompletion 总结 DispatcherServlet 去调用 HandlerExecutionChian 去调用 拦截器具体方法. 复杂点是添加一个拦截器到被加入到 HandlerExecutionChian 比较复杂一点, 以及带路径匹配的拦截器实现略复杂一些.
默认的 HandlerMapping/HandlerAdapter 在何时加入? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 org.springframework.web.servlet.DispatcherServlet#initStrategies protected void initStrategies (ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
参数是如何与 HTTP 请求 body 绑定的(序列化, 格式化, 绑定)? 1 2 3 4 5 1.在适配器执行 handler 的时候, 即 RequestMappingHandlerAdapter#invokeHandlerMethod 2.此方法会进行一些其他处理, 然后准备执行方法前解析参数, 即在 InvocableHandlerMethod#getMethodArgumentValues() 中 3 .这个方法中会遍历每一个参数, 再遍历配置的所有 resolvers, 通过 supportsParameter 接口判断是否支持参数解析, 是则调用 resolveArgument 接口获得实参4.这其中, 最重要的是 resolvers, 其一般在 RequestMappingHandlerAdapter#getDefaultArgumentResolvers() 中添加默认和用户自定义的 resoloves. 5.如 RequestResponseBodyMethodProcessor#resolveArgument() 用于处理带 @RequestBody 注解的参数.
总结: 适配器执行具体方法前, 先用反射获取这个方法的 参数(形参)集合, 挨个遍历从 resolvers 找支持解析的类来解析, 得到的返回值作为实参先存起来, 最后调用具体方法时就可以带上实参们执行就实现了将 HTTP 的数据绑定到 controller 的方法参数上的功能.
参考 RequestResponseBodyMethodProcessor#resolveArgument()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override 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); }
参数校验是如何进行的? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 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()); } } protected void validateIfApplicable (WebDataBinder binder, MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class ) ; if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid" )) { Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); binder.validate(validationHints); break ; } } } public void validate (Object... validationHints) { Object target = getTarget(); Assert.state(target != null , "No target to validate" ); BindingResult bindingResult = getBindingResult(); for (Validator validator : getValidators()) { if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { ((SmartValidator) validator).validate(target, bindingResult, validationHints); } else if (validator != null ) { validator.validate(target, bindingResult); } } }
有关参数与返回值的一些拦截与干预(@RequestBody, @ResponseBody). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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); } }
由上可知, argumentResolvers/returnValueHandlers 都是此时初始化的, 再点进去看代码发现, 除了默认的, 还有用户自定义的 customArgumentResolvers/customReturnValueHandlers;
1 2 3 4 WebMvcConfigurationSupport#addReturnValueHandlers WebMvcConfigurationSupport#addArgumentResolvers
除此之外, 两边的默认值都有 RequestResponseBodyMethodProcessor, 这就是用于处理@RequestBody/@ResponseBody 的了, 往里面深入的看, 能看到其使用 messageConverters 读取 body 数据, 然后也会在对应的地方触发 advice 的方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (HttpMessageConverter<?> converter : this .messageConverters) { Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass(); GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null ); if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass != null && converter.canRead(targetClass, contentType))) { if (message.hasBody()) { HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse)); body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType); } else { body = getAdvice().handleEmptyBody(null , message, parameter, targetType, converterType); } break ; } }
参数的N种绑定方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers () { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); 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 (getCustomArgumentResolvers() != null ) { resolvers.addAll(getCustomArgumentResolvers()); } resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true )); resolvers.add(new ServletModelAttributeMethodProcessor(true )); return resolvers; }
看几个常见的
1 2 3 4 5 6 7 8 1.RequestParamMethodArgumentResolver: 负责解析带 @RequestParam 注解的普通参数 2.RequestParamMapMethodArgumentResolver: 负责解析带 @RequestParam 的 Map 参数.... 3.PathVariableMethodArgumentResolver/PathVariableMapMethodArgumentResolver: 同上解析带 @PathVariable 注解的参数 4.RequestResponseBodyMethodProcessor: 负责解析带 @RequestBody 的参数 5.RequestHeaderMethodArgumentResolver: 负责解析带 @RequestHeader 的参数 (表示没用过) 6.ServletRequestMethodArgumentResolver: 负责解析 HttpServletRequest 等类型的参数(即 req, 用的贼多) 7.ServletResponseMethodArgumentResolver: 负责解析 HttpServletResponse 等类型的参数(即 resp, 下载接口没少用) 8.RequestParamMethodArgumentResolver: 啥都解析.... 即不带任何注解的普通类型.