当前位置: 首页 > news >正文

建网站的方法网页设计项目案例网站

建网站的方法,网页设计项目案例网站,惠州网站搭建,重庆seo网站推广工具文章目录 WEB20) RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter演示1 - DispatcherServlet 初始化代码参考 收获#x1f4a1;演示2 - 自定义参数与返回值处理器代码参考 收获#x1f4a1; 21) 参数解析器演示 - 常见参数解析器代码参考 收获#x1f4a1; 2… 文章目录 WEB20) RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter演示1 - DispatcherServlet 初始化代码参考 收获演示2 - 自定义参数与返回值处理器代码参考 收获 21) 参数解析器演示 - 常见参数解析器代码参考 收获 22) 参数名解析演示 - 两种方法获取参数名代码参考 收获 23) 对象绑定与类型转换底层第一套转换接口与实现底层第二套转换接口高层接口与实现演示1 - 类型转换与数据绑定代码参考 收获演示2 - 数据绑定工厂代码参考 收获演示3 - 获取泛型参数代码参考 收获 24) ControllerAdvice 之 InitBinder演示 - 准备 InitBinder收获 25) 控制器方法执行流程图1图2图3 26) ControllerAdvice 之 ModelAttribute演示 - 准备 ModelAttribute代码参考 收获 27) 返回值处理器演示 - 常见返回值处理器代码参考 收获 28) MessageConverter演示 - MessageConverter 的作用代码参考 收获 29) ControllerAdvice 之 ResponseBodyAdvice演示 - ResponseBodyAdvice 增强代码参考 收获 30) 异常解析器演示 - ExceptionHandlerExceptionResolver代码参考 收获 31) ControllerAdvice 之 ExceptionHandler演示 - 准备 ExceptionHandler代码参考 收获 32) Tomcat 异常处理演示1 - 错误页处理关键代码 收获演示2 - BasicErrorController关键代码 收获 33) BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter演示 - 本组映射器和适配器关键代码 收获 34) RouterFunctionMapping 与 HandlerFunctionAdapter演示 - 本组映射器和适配器关键代码 收获 35) SimpleUrlHandlerMapping 与 HttpRequestHandlerAdapter演示1 - 本组映射器和适配器代码参考关键代码 收获演示2 - 静态资源解析优化关键代码 收获演示3 - 欢迎页关键代码 收获映射器与适配器小结 36) mvc 处理流程 WEB 20) RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 俩是一对分别用来 处理 RequestMapping 映射调用控制器方法、并处理方法参数与方法返回值 演示1 - DispatcherServlet 初始化 代码参考 package com.itheima.a20;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map;public class A20 {private static final Logger log LoggerFactory.getLogger(A20.class);public static void main(String[] args) throws Exception {AnnotationConfigServletWebServerApplicationContext context new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);// 作用 解析 RequestMapping 以及派生注解生成路径与控制器方法的映射关系, 在初始化时就生成RequestMappingHandlerMapping handlerMapping context.getBean(RequestMappingHandlerMapping.class);// 获取映射结果MapRequestMappingInfo, HandlerMethod handlerMethods handlerMapping.getHandlerMethods();handlerMethods.forEach((k, v) - {System.out.println(k v);});// 请求来了获取控制器方法 返回处理器执行链对象MockHttpServletRequest request new MockHttpServletRequest(GET, /test4);request.setParameter(name, 张三);request.addHeader(token, 某个令牌);MockHttpServletResponse response new MockHttpServletResponse();HandlerExecutionChain chain handlerMapping.getHandler(request);System.out.println(chain);System.out.println();// HandlerAdapter 作用: 调用控制器方法MyRequestMappingHandlerAdapter handlerAdapter context.getBean(MyRequestMappingHandlerAdapter.class);handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());// 检查响应byte[] content response.getContentAsByteArray();System.out.println(new String(content, StandardCharsets.UTF_8));/*System.out.println( 所有参数解析器);for (HandlerMethodArgumentResolver resolver : handlerAdapter.getArgumentResolvers()) {System.out.println(resolver);}System.out.println( 所有返回值解析器);for (HandlerMethodReturnValueHandler handler : handlerAdapter.getReturnValueHandlers()) {System.out.println(handler);}*//*学到了什么a. DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化b. 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等本章介绍两个最为重要的组件a. RequestMappingHandlerAdapter, 以 RequestMapping 作为映射路径b. RequestMappingHandlerAdapter, 调用 handlerc. 控制器的具体方法会被当作 handler- handler 的参数和返回值多种多样- 需要解析方法参数, 由 HandlerMethodArgumentResolver 来做- 需要处理方法返回值, 由 HandlerMethodReturnValueHandler 来做*/} } package com.itheima.a20;import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List;Configuration ComponentScan PropertySource(classpath:application.properties) EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) public class WebConfig {// ⬅️内嵌 web 容器工厂Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {return new TomcatServletWebServerFactory(serverProperties.getPort());}// ⬅️创建 DispatcherServletBeanpublic DispatcherServlet dispatcherServlet() {return new DispatcherServlet();}// ⬅️注册 DispatcherServlet, Spring MVC 的入口Beanpublic DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) {DispatcherServletRegistrationBean registrationBean new DispatcherServletRegistrationBean(dispatcherServlet, /);registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());return registrationBean;}// 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 bean, 给测试带来困扰// ⬅️1. 加入RequestMappingHandlerMappingBeanpublic RequestMappingHandlerMapping requestMappingHandlerMapping() {return new RequestMappingHandlerMapping();}// ⬅️2. 继续加入RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapterBeanpublic MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {TokenArgumentResolver tokenArgumentResolver new TokenArgumentResolver();YmlReturnValueHandler ymlReturnValueHandler new YmlReturnValueHandler();MyRequestMappingHandlerAdapter handlerAdapter new MyRequestMappingHandlerAdapter();handlerAdapter.setCustomArgumentResolvers(List.of(tokenArgumentResolver));handlerAdapter.setCustomReturnValueHandlers(List.of(ymlReturnValueHandler));return handlerAdapter;}public HttpMessageConverters httpMessageConverters() {return new HttpMessageConverters();}// ⬅️3. 演示 RequestMappingHandlerAdapter 初始化后, 有哪些参数、返回值处理器// ⬅️3.1 创建自定义参数处理器// ⬅️3.2 创建自定义返回值处理器} 收获 DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等并逐一调用它们的初始化RequestMappingHandlerMapping 初始化时会收集所有 RequestMapping 映射信息封装为 Map其中 key 是 RequestMappingInfo 类型包括请求路径、请求方法等信息value 是 HandlerMethod 类型包括控制器方法对象、控制器对象有了这个 Map就可以在请求到达时快速完成映射找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet RequestMappingHandlerAdapter 初始化时会准备 HandlerMethod 调用时需要的各个组件如 HandlerMethodArgumentResolver 解析控制器方法参数HandlerMethodReturnValueHandler 处理控制器方法返回值 演示2 - 自定义参数与返回值处理器 代码参考 package com.itheima.a20;import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer;public class TokenArgumentResolver implements HandlerMethodArgumentResolver {Override// 是否支持某个参数public boolean supportsParameter(MethodParameter parameter) {Token token parameter.getParameterAnnotation(Token.class);return token ! null;}Override// 解析参数public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {return webRequest.getHeader(token);} } package com.itheima.a20;import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.yaml.snakeyaml.Yaml;import javax.servlet.http.HttpServletResponse;public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {Overridepublic boolean supportsReturnType(MethodParameter returnType) {Yml yml returnType.getMethodAnnotation(Yml.class);return yml ! null;}Override // 返回值public void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 1. 转换返回结果为 yaml 字符串String str new Yaml().dump(returnValue);// 2. 将 yaml 字符串写入响应HttpServletResponse response webRequest.getNativeResponse(HttpServletResponse.class);response.setContentType(text/plain;charsetutf-8);response.getWriter().print(str);// 3. 设置请求已经处理完毕mavContainer.setRequestHandled(true);} } 收获 体会参数解析器的作用体会返回值处理器的作用 21) 参数解析器 演示 - 常见参数解析器 代码参考 package com.itheima.a21;import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockPart; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver; import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver; import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.support.StandardServletMultipartResolver; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.*;import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors;/*目标: 解析控制器方法的参数值常见的参数处理器如下:org.springframework.web.method.annotation.RequestParamMethodArgumentResolverabbc908org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver44afefd5org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver9a7a808org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver72209d93org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver2687f956org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver1ded7b14org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor29be7749org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor5f84abe8org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver4650a407org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver30135202org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver6a4d7f76org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver10ec523corg.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver53dfacbaorg.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver79767781org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver78411116org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolveraced190org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver245a060forg.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor6edaa77aorg.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver1e63d216org.springframework.web.method.annotation.ModelMethodProcessor62ddd21borg.springframework.web.method.annotation.MapMethodProcessor16c3ca31org.springframework.web.method.annotation.ErrorsMethodArgumentResolver2d195ee4org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver2d6aca33org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver21ab988forg.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver29314cc9org.springframework.web.method.annotation.RequestParamMethodArgumentResolver4e38d975org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor35f8a9d3*/ public class A21 {public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);DefaultListableBeanFactory beanFactory context.getDefaultListableBeanFactory();// 准备测试 RequestHttpServletRequest request mockRequest();// 要点1. 控制器方法被封装为 HandlerMethodHandlerMethod handlerMethod new HandlerMethod(new Controller(), Controller.class.getMethod(test, String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));// 要点2. 准备对象绑定与类型转换ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(null, null);// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果ModelAndViewContainer container new ModelAndViewContainer();// 要点4. 解析每个参数值for (MethodParameter parameter : handlerMethod.getMethodParameters()) {// 多个解析器组合HandlerMethodArgumentResolverComposite composite new HandlerMethodArgumentResolverComposite();composite.addResolvers(// false 表示必须有 RequestParamnew RequestParamMethodArgumentResolver(beanFactory, false),new PathVariableMethodArgumentResolver(),new RequestHeaderMethodArgumentResolver(beanFactory),new ServletCookieValueMethodArgumentResolver(beanFactory),new ExpressionValueMethodArgumentResolver(beanFactory),new ServletRequestMethodArgumentResolver(),new ServletModelAttributeMethodProcessor(false), // 必须有 ModelAttributenew RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),new ServletModelAttributeMethodProcessor(true), // 省略了 ModelAttributenew RequestParamMethodArgumentResolver(beanFactory, true) // 省略 RequestParam);String annotations Arrays.stream(parameter.getParameterAnnotations()).map(a - a.annotationType().getSimpleName()).collect(Collectors.joining());String str annotations.length() 0 ? annotations : ;parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());if (composite.supportsParameter(parameter)) {// 支持此参数Object v composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory); // System.out.println(v.getClass());System.out.println([ parameter.getParameterIndex() ] str parameter.getParameterType().getSimpleName() parameter.getParameterName() - v);System.out.println(模型数据为 container.getModel());} else {System.out.println([ parameter.getParameterIndex() ] str parameter.getParameterType().getSimpleName() parameter.getParameterName());}}/*学到了什么a. 每个参数处理器能干啥1) 看是否支持某种参数2) 获取参数的值b. 组合模式在 Spring 中的体现c. RequestParam, CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取*/}private static HttpServletRequest mockRequest() {MockHttpServletRequest request new MockHttpServletRequest();request.setParameter(name1, zhangsan);request.setParameter(name2, lisi);request.addPart(new MockPart(file, abc, hello.getBytes(StandardCharsets.UTF_8)));MapString, String map new AntPathMatcher().extractUriTemplateVariables(/test/{id}, /test/123);System.out.println(map);request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);request.setContentType(application/json);request.setCookies(new Cookie(token, 123456));request.setParameter(name, 张三);request.setParameter(age, 18);request.setContent({name:李四,age:20}.getBytes(StandardCharsets.UTF_8));return new StandardServletMultipartResolver().resolveMultipart(request);}static class Controller {public void test(RequestParam(name1) String name1, // name1张三String name2, // name2李四RequestParam(age) int age, // age18RequestParam(name home, defaultValue ${JAVA_HOME}) String home1, // spring 获取数据RequestParam(file) MultipartFile file, // 上传文件PathVariable(id) int id, // /test/124 /test/{id}RequestHeader(Content-Type) String header,CookieValue(token) String token,Value(${JAVA_HOME}) String home2, // spring 获取数据 ${} #{}HttpServletRequest request, // request, response, session ...ModelAttribute(abc) User user1, // namezhangage18User user2, // namezhangage18RequestBody User user3 // json) {}}static class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}Overridepublic String toString() {return User{ name name \ , age age };}} } 收获 初步了解 RequestMappingHandlerAdapter 的调用过程 控制器方法被封装为 HandlerMethod准备对象绑定与类型转换准备 ModelAndViewContainer 用来存储中间 Model 结果解析每个参数值 解析参数依赖的就是各种参数解析器它们都有两个重要方法 supportsParameter 判断是否支持方法参数resolveArgument 解析方法参数 常见参数的解析 RequestParam省略 RequestParamRequestParam(defaultValue)MultipartFilePathVariableRequestHeaderCookieValueValueHttpServletRequest 等ModelAttribute省略 ModelAttributeRequestBody 组合模式在 Spring 中的体现RequestParam, CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取 22) 参数名解析 演示 - 两种方法获取参数名 代码参考 package com.itheima.a22;import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.StandardReflectionParameterNameDiscoverer;import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays;/*目标: 如何获取方法参数名, 注意把 a22 目录添加至模块的类路径1. a22 不在 src 是避免 idea 自动编译它下面的类2. spring boot 在编译时会加 -parameters3. 大部分 IDE 编译时都会加 -g*/ public class A22 {public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {// 1. 反射获取参数名Method foo Bean2.class.getMethod(foo, String.class, int.class);/*for (Parameter parameter : foo.getParameters()) {System.out.println(parameter.getName());}*/// 2. 基于 LocalVariableTable 本地变量表LocalVariableTableParameterNameDiscoverer discoverer new LocalVariableTableParameterNameDiscoverer();String[] parameterNames discoverer.getParameterNames(foo);System.out.println(Arrays.toString(parameterNames));/*学到了什么a. 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名b. 如果编译时添加了 -g 可以生成调试信息, 但分为两种情况1. 普通类, 会包含局部变量表, 用 asm 可以拿到参数名2. 接口, 不会包含局部变量表, 无法获得参数名 (这也是 MyBatis 在实现 Mapper 接口时为何要提供 Param 注解来辅助获得参数名)*/}} 收获 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名如果编译时添加了 -g 可以生成调试信息, 但分为两种情况 普通类, 会包含局部变量表, 用 asm 可以拿到参数名接口, 不会包含局部变量表, 无法获得参数名 这也是 MyBatis 在实现 Mapper 接口时为何要提供 Param 注解来辅助获得参数名 23) 对象绑定与类型转换 底层第一套转换接口与实现 #mermaid-svg-dYHArAsubSLfSsyK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dYHArAsubSLfSsyK .error-icon{fill:#552222;}#mermaid-svg-dYHArAsubSLfSsyK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dYHArAsubSLfSsyK .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-dYHArAsubSLfSsyK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dYHArAsubSLfSsyK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dYHArAsubSLfSsyK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dYHArAsubSLfSsyK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dYHArAsubSLfSsyK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dYHArAsubSLfSsyK .marker.cross{stroke:#333333;}#mermaid-svg-dYHArAsubSLfSsyK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dYHArAsubSLfSsyK g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-dYHArAsubSLfSsyK g.classGroup text .title{font-weight:bolder;}#mermaid-svg-dYHArAsubSLfSsyK .nodeLabel,#mermaid-svg-dYHArAsubSLfSsyK .edgeLabel{color:#131300;}#mermaid-svg-dYHArAsubSLfSsyK .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-dYHArAsubSLfSsyK .label text{fill:#131300;}#mermaid-svg-dYHArAsubSLfSsyK .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-dYHArAsubSLfSsyK .classTitle{font-weight:bolder;}#mermaid-svg-dYHArAsubSLfSsyK .node rect,#mermaid-svg-dYHArAsubSLfSsyK .node circle,#mermaid-svg-dYHArAsubSLfSsyK .node ellipse,#mermaid-svg-dYHArAsubSLfSsyK .node polygon,#mermaid-svg-dYHArAsubSLfSsyK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dYHArAsubSLfSsyK .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-dYHArAsubSLfSsyK g.clickable{cursor:pointer;}#mermaid-svg-dYHArAsubSLfSsyK g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-dYHArAsubSLfSsyK g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-dYHArAsubSLfSsyK .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-dYHArAsubSLfSsyK .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-dYHArAsubSLfSsyK .dashed-line{stroke-dasharray:3;}#mermaid-svg-dYHArAsubSLfSsyK #compositionStart,#mermaid-svg-dYHArAsubSLfSsyK .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #compositionEnd,#mermaid-svg-dYHArAsubSLfSsyK .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #dependencyStart,#mermaid-svg-dYHArAsubSLfSsyK .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #dependencyStart,#mermaid-svg-dYHArAsubSLfSsyK .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #extensionStart,#mermaid-svg-dYHArAsubSLfSsyK .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #extensionEnd,#mermaid-svg-dYHArAsubSLfSsyK .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #aggregationStart,#mermaid-svg-dYHArAsubSLfSsyK .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK #aggregationEnd,#mermaid-svg-dYHArAsubSLfSsyK .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dYHArAsubSLfSsyK .edgeTerminals{font-size:11px;}#mermaid-svg-dYHArAsubSLfSsyK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} «interface» Formatter «interface» Printer «interface» Parser Converters SetGenericConverter «interface» Converter «interface» ConversionService FormattingConversionService Adapter1 Adapter2 Adapter3 Printer 把其它类型转为 StringParser 把 String 转为其它类型Formatter 综合 Printer 与 Parser 功能Converter 把类型 S 转为类型 TPrinter、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合FormattingConversionService 利用其它们实现转换 底层第二套转换接口 #mermaid-svg-zjuZ2RINh6FsYbX2 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zjuZ2RINh6FsYbX2 .error-icon{fill:#552222;}#mermaid-svg-zjuZ2RINh6FsYbX2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zjuZ2RINh6FsYbX2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zjuZ2RINh6FsYbX2 .marker.cross{stroke:#333333;}#mermaid-svg-zjuZ2RINh6FsYbX2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zjuZ2RINh6FsYbX2 g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-zjuZ2RINh6FsYbX2 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-zjuZ2RINh6FsYbX2 .nodeLabel,#mermaid-svg-zjuZ2RINh6FsYbX2 .edgeLabel{color:#131300;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-zjuZ2RINh6FsYbX2 .label text{fill:#131300;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-zjuZ2RINh6FsYbX2 .classTitle{font-weight:bolder;}#mermaid-svg-zjuZ2RINh6FsYbX2 .node rect,#mermaid-svg-zjuZ2RINh6FsYbX2 .node circle,#mermaid-svg-zjuZ2RINh6FsYbX2 .node ellipse,#mermaid-svg-zjuZ2RINh6FsYbX2 .node polygon,#mermaid-svg-zjuZ2RINh6FsYbX2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zjuZ2RINh6FsYbX2 .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 g.clickable{cursor:pointer;}#mermaid-svg-zjuZ2RINh6FsYbX2 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-zjuZ2RINh6FsYbX2 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-zjuZ2RINh6FsYbX2 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-zjuZ2RINh6FsYbX2 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-zjuZ2RINh6FsYbX2 .dashed-line{stroke-dasharray:3;}#mermaid-svg-zjuZ2RINh6FsYbX2 #compositionStart,#mermaid-svg-zjuZ2RINh6FsYbX2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #compositionEnd,#mermaid-svg-zjuZ2RINh6FsYbX2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #dependencyStart,#mermaid-svg-zjuZ2RINh6FsYbX2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #dependencyStart,#mermaid-svg-zjuZ2RINh6FsYbX2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #extensionStart,#mermaid-svg-zjuZ2RINh6FsYbX2 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #extensionEnd,#mermaid-svg-zjuZ2RINh6FsYbX2 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #aggregationStart,#mermaid-svg-zjuZ2RINh6FsYbX2 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 #aggregationEnd,#mermaid-svg-zjuZ2RINh6FsYbX2 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zjuZ2RINh6FsYbX2 .edgeTerminals{font-size:11px;}#mermaid-svg-zjuZ2RINh6FsYbX2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 多 «interface» PropertyEditorRegistry «interface» PropertyEditor PropertyEditor 把 String 与其它类型相互转换PropertyEditorRegistry 可以注册多个 PropertyEditor 对象与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配 高层接口与实现 #mermaid-svg-jqDt7z7XxSoMOUeA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jqDt7z7XxSoMOUeA .error-icon{fill:#552222;}#mermaid-svg-jqDt7z7XxSoMOUeA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jqDt7z7XxSoMOUeA .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-jqDt7z7XxSoMOUeA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jqDt7z7XxSoMOUeA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jqDt7z7XxSoMOUeA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jqDt7z7XxSoMOUeA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jqDt7z7XxSoMOUeA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jqDt7z7XxSoMOUeA .marker.cross{stroke:#333333;}#mermaid-svg-jqDt7z7XxSoMOUeA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jqDt7z7XxSoMOUeA g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-jqDt7z7XxSoMOUeA g.classGroup text .title{font-weight:bolder;}#mermaid-svg-jqDt7z7XxSoMOUeA .nodeLabel,#mermaid-svg-jqDt7z7XxSoMOUeA .edgeLabel{color:#131300;}#mermaid-svg-jqDt7z7XxSoMOUeA .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-jqDt7z7XxSoMOUeA .label text{fill:#131300;}#mermaid-svg-jqDt7z7XxSoMOUeA .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-jqDt7z7XxSoMOUeA .classTitle{font-weight:bolder;}#mermaid-svg-jqDt7z7XxSoMOUeA .node rect,#mermaid-svg-jqDt7z7XxSoMOUeA .node circle,#mermaid-svg-jqDt7z7XxSoMOUeA .node ellipse,#mermaid-svg-jqDt7z7XxSoMOUeA .node polygon,#mermaid-svg-jqDt7z7XxSoMOUeA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jqDt7z7XxSoMOUeA .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-jqDt7z7XxSoMOUeA g.clickable{cursor:pointer;}#mermaid-svg-jqDt7z7XxSoMOUeA g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-jqDt7z7XxSoMOUeA g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-jqDt7z7XxSoMOUeA .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-jqDt7z7XxSoMOUeA .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-jqDt7z7XxSoMOUeA .dashed-line{stroke-dasharray:3;}#mermaid-svg-jqDt7z7XxSoMOUeA #compositionStart,#mermaid-svg-jqDt7z7XxSoMOUeA .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #compositionEnd,#mermaid-svg-jqDt7z7XxSoMOUeA .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #dependencyStart,#mermaid-svg-jqDt7z7XxSoMOUeA .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #dependencyStart,#mermaid-svg-jqDt7z7XxSoMOUeA .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #extensionStart,#mermaid-svg-jqDt7z7XxSoMOUeA .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #extensionEnd,#mermaid-svg-jqDt7z7XxSoMOUeA .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #aggregationStart,#mermaid-svg-jqDt7z7XxSoMOUeA .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA #aggregationEnd,#mermaid-svg-jqDt7z7XxSoMOUeA .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jqDt7z7XxSoMOUeA .edgeTerminals{font-size:11px;}#mermaid-svg-jqDt7z7XxSoMOUeA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} «interface» TypeConverter SimpleTypeConverter BeanWrapperImpl DirectFieldAccessor ServletRequestDataBinder TypeConverterDelegate «interface» ConversionService «interface» PropertyEditorRegistry 它们都实现了 TypeConverter 这个高层转换接口在转换时会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换Facade 门面模式 首先看是否有自定义转换器, InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)再看有没有 ConversionService 转换再利用默认的 PropertyEditor 转换最后有一些特殊处理 SimpleTypeConverter 仅做类型转换BeanWrapperImpl 为 bean 的属性赋值当需要时做类型转换走 PropertyDirectFieldAccessor 为 bean 的属性赋值当需要时做类型转换走 FieldServletRequestDataBinder 为 bean 的属性执行绑定当需要时做类型转换根据 directFieldAccess 选择走 Property 还是 Field具备校验与获取校验结果功能 演示1 - 类型转换与数据绑定 代码参考 package com.itheima.a23;import org.springframework.beans.SimpleTypeConverter;import java.util.Date;public class TestSimpleConverter {public static void main(String[] args) {// 仅有类型转换的功能SimpleTypeConverter typeConverter new SimpleTypeConverter();Integer number typeConverter.convertIfNecessary(13, int.class);Date date typeConverter.convertIfNecessary(1999/03/04, Date.class);System.out.println(number);System.out.println(date);} } package com.itheima.a23;import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.SimpleTypeConverter; import org.springframework.core.GenericTypeResolver; import org.springframework.format.Formatter; import org.springframework.format.support.FormatterPropertyEditorAdapter; import org.springframework.format.support.FormattingConversionService;import java.util.Date;public class TestBeanWrapper {public static void main(String[] args) {// 利用反射原理, 为 bean 的属性赋值MyBean target new MyBean();BeanWrapperImpl wrapper new BeanWrapperImpl(target);wrapper.setPropertyValue(a, 10);wrapper.setPropertyValue(b, hello);wrapper.setPropertyValue(c, 1999/03/04);System.out.println(target);}static class MyBean {private int a;private String b;private Date c;public int getA() {return a;}public void setA(int a) {this.a a;}public String getB() {return b;}public void setB(String b) {this.b b;}public Date getC() {return c;}public void setC(Date c) {this.c c;}Overridepublic String toString() {return MyBean{ a a , b b \ , c c };}} } package com.itheima.a23;import org.springframework.beans.MutablePropertyValues; import org.springframework.validation.DataBinder;import java.util.Date;public class TestDataBinder {public static void main(String[] args) {// 执行数据绑定MyBean target new MyBean();DataBinder dataBinder new DataBinder(target);dataBinder.initDirectFieldAccess();MutablePropertyValues pvs new MutablePropertyValues();pvs.add(a, 10);pvs.add(b, hello);pvs.add(c, 1999/03/04);dataBinder.bind(pvs);System.out.println(target);}static class MyBean {private int a;private String b;private Date c;Overridepublic String toString() {return MyBean{ a a , b b \ , c c };}} } package com.itheima.a23;import org.springframework.beans.MutablePropertyValues; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.validation.DataBinder; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.ServletRequestParameterPropertyValues;import java.util.Date;public class TestServletDataBinder {public static void main(String[] args) {// web 环境下数据绑定MyBean target new MyBean();ServletRequestDataBinder dataBinder new ServletRequestDataBinder(target);MockHttpServletRequest request new MockHttpServletRequest();request.setParameter(a, 10);request.setParameter(b, hello);request.setParameter(c, 1999/03/04);dataBinder.bind(new ServletRequestParameterPropertyValues(request));System.out.println(target);}static class MyBean {private int a;private String b;private Date c;public int getA() {return a;}public void setA(int a) {this.a a;}public String getB() {return b;}public void setB(String b) {this.b b;}public Date getC() {return c;}public void setC(Date c) {this.c c;}Overridepublic String toString() {return MyBean{ a a , b b \ , c c };}} } package com.itheima.a23;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.GenericTypeResolver; import org.springframework.format.Formatter;import java.lang.reflect.Type; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;public class MyDateFormatter implements FormatterDate {private static final Logger log LoggerFactory.getLogger(MyDateFormatter.class);private final String desc;public MyDateFormatter(String desc) {this.desc desc;}Overridepublic String print(Date date, Locale locale) {SimpleDateFormat sdf new SimpleDateFormat(yyyy|MM|dd);return sdf.format(date);}Overridepublic Date parse(String text, Locale locale) throws ParseException {log.debug( 进入了: {}, desc);SimpleDateFormat sdf new SimpleDateFormat(yyyy|MM|dd);return sdf.parse(text);}} 收获 基本的类型转换与数据绑定用法 SimpleTypeConverterBeanWrapperImplDirectFieldAccessorServletRequestDataBinder 演示2 - 数据绑定工厂 代码参考 package com.itheima.a23;import org.springframework.boot.convert.ApplicationConversionService; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.bind.ServletRequestParameterPropertyValues; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;import java.util.Date;public class TestServletDataBinderFactory {public static void main(String[] args) throws Exception {MockHttpServletRequest request new MockHttpServletRequest();request.setParameter(birthday, 1999|01|02);request.setParameter(address.name, 西安);User target new User();// 1. 用工厂, 无转换功能 // ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(null, null);// 2. 用 InitBinder 转换 PropertyEditorRegistry PropertyEditor // InvocableHandlerMethod method new InvocableHandlerMethod(new MyController(), MyController.class.getMethod(aaa, WebDataBinder.class)); // ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(List.of(method), null);// 3. 用 ConversionService 转换 ConversionService Formatter // FormattingConversionService service new FormattingConversionService(); // service.addFormatter(new MyDateFormatter(用 ConversionService 方式扩展转换功能)); // ConfigurableWebBindingInitializer initializer new ConfigurableWebBindingInitializer(); // initializer.setConversionService(service); // ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(null, initializer);// 4. 同时加了 InitBinder 和 ConversionService // InvocableHandlerMethod method new InvocableHandlerMethod(new MyController(), MyController.class.getMethod(aaa, WebDataBinder.class)); // // FormattingConversionService service new FormattingConversionService(); // service.addFormatter(new MyDateFormatter(用 ConversionService 方式扩展转换功能)); // ConfigurableWebBindingInitializer initializer new ConfigurableWebBindingInitializer(); // initializer.setConversionService(service); // // ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(List.of(method), initializer);// 5. 使用默认 ConversionService 转换ApplicationConversionService service new ApplicationConversionService();ConfigurableWebBindingInitializer initializer new ConfigurableWebBindingInitializer();initializer.setConversionService(service);ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(null, initializer);WebDataBinder dataBinder factory.createBinder(new ServletWebRequest(request), target, user);dataBinder.bind(new ServletRequestParameterPropertyValues(request));System.out.println(target);}static class MyController {InitBinderpublic void aaa(WebDataBinder dataBinder) {// 扩展 dataBinder 的转换器dataBinder.addCustomFormatter(new MyDateFormatter(用 InitBinder 方式扩展的));}}public static class User {DateTimeFormat(pattern yyyy|MM|dd)private Date birthday;private Address address;public Address getAddress() {return address;}public void setAddress(Address address) {this.address address;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday birthday;}Overridepublic String toString() {return User{ birthday birthday , address address };}}public static class Address {private String name;public String getName() {return name;}public void setName(String name) {this.name name;}Overridepublic String toString() {return Address{ name name \ };}} } 收获 ServletRequestDataBinderFactory 的用法和扩展点 可以解析控制器的 InitBinder 标注方法作为扩展点添加自定义转换器 控制器私有范围 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点添加自定义转换器 公共范围 同时加了 InitBinder 和 ConversionService 的转换优先级 优先采用 InitBinder 的转换器其次使用 ConversionService 的转换器使用默认转换器特殊处理例如有参构造 演示3 - 获取泛型参数 代码参考 package com.itheima.a23.sub;import org.springframework.core.GenericTypeResolver; import org.springframework.core.ResolvableType;import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;public class TestGenericType {public static void main(String[] args) {// 小技巧// 1. java apiSystem.out.println();Type type TeacherDao.class.getGenericSuperclass();System.out.println(type);if (type instanceof ParameterizedType parameterizedType) {System.out.println(parameterizedType.getActualTypeArguments()[0]);}// 2. spring api 1System.out.println();Class? t GenericTypeResolver.resolveTypeArgument(TeacherDao.class, BaseDao.class);System.out.println(t);// 3. spring api 2System.out.println();System.out.println(ResolvableType.forClass(TeacherDao.class).getSuperType().getGeneric().resolve());}} 收获 java api 获取泛型参数spring api 获取泛型参数 24) ControllerAdvice 之 InitBinder 演示 - 准备 InitBinder 准备 InitBinder 在整个 HandlerAdapter 调用过程中所处的位置 #mermaid-svg-45qsHlKrQWUWIyC8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 .error-icon{fill:#552222;}#mermaid-svg-45qsHlKrQWUWIyC8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-45qsHlKrQWUWIyC8 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-45qsHlKrQWUWIyC8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-45qsHlKrQWUWIyC8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-45qsHlKrQWUWIyC8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-45qsHlKrQWUWIyC8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-45qsHlKrQWUWIyC8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-45qsHlKrQWUWIyC8 .marker.cross{stroke:#333333;}#mermaid-svg-45qsHlKrQWUWIyC8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-45qsHlKrQWUWIyC8 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-45qsHlKrQWUWIyC8 text.actortspan{fill:black;stroke:none;}#mermaid-svg-45qsHlKrQWUWIyC8 .actor-line{stroke:grey;}#mermaid-svg-45qsHlKrQWUWIyC8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 .sequenceNumber{fill:white;}#mermaid-svg-45qsHlKrQWUWIyC8 #sequencenumber{fill:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 .messageText{fill:#333;stroke:#333;}#mermaid-svg-45qsHlKrQWUWIyC8 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-45qsHlKrQWUWIyC8 .labelText,#mermaid-svg-45qsHlKrQWUWIyC8 .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-45qsHlKrQWUWIyC8 .loopText,#mermaid-svg-45qsHlKrQWUWIyC8 .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-45qsHlKrQWUWIyC8 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-45qsHlKrQWUWIyC8 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-45qsHlKrQWUWIyC8 .noteText,#mermaid-svg-45qsHlKrQWUWIyC8 .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-45qsHlKrQWUWIyC8 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-45qsHlKrQWUWIyC8 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-45qsHlKrQWUWIyC8 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-45qsHlKrQWUWIyC8 .actorPopupMenu{position:absolute;}#mermaid-svg-45qsHlKrQWUWIyC8 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-45qsHlKrQWUWIyC8 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-45qsHlKrQWUWIyC8 .actor-man circle,#mermaid-svg-45qsHlKrQWUWIyC8 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-45qsHlKrQWUWIyC8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 准备 InitBinder 准备 ModelAttribute 添加Model数据 invokeAndHandle 获取 args 有的解析器涉及 RequestBodyAdvice 有的解析器涉及数据绑定生成Model数据 args method.invoke(bean,args) 得到 returnValue 处理 returnValue 有的处理器涉及 ResponseBodyAdvice 添加Model数据,处理视图名,是否渲染等 获取 ModelAndView HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer RequestMappingHandlerAdapter 在图中缩写为 HandlerAdapterHandlerMethodArgumentResolverComposite 在图中缩写为 ArgumentResolversHandlerMethodReturnValueHandlerComposite 在图中缩写为 ReturnValueHandlers package com.itheima.a24;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.web.method.ControllerAdviceBean; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; import java.util.stream.Collectors;/*InitBinder 的来源*/ public class A24 {private static final Logger log LoggerFactory.getLogger(A24.class);public static void main(String[] args) throws Exception {/*InitBinder 的来源有两个1. ControllerAdvice 中 InitBinder 标注的方法由 RequestMappingHandlerAdapter 在初始化时解析并记录2. Controller 中 InitBinder 标注的方法由 RequestMappingHandlerAdapter 会在控制器方法首次执行时解析并记录*/AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);RequestMappingHandlerAdapter handlerAdapter new RequestMappingHandlerAdapter();handlerAdapter.setApplicationContext(context);handlerAdapter.afterPropertiesSet();log.debug(1. 刚开始...);showBindMethods(handlerAdapter);Method getDataBinderFactory RequestMappingHandlerAdapter.class.getDeclaredMethod(getDataBinderFactory, HandlerMethod.class);getDataBinderFactory.setAccessible(true);log.debug(2. 模拟调用 Controller1 的 foo 方法时 ...);getDataBinderFactory.invoke(handlerAdapter, new HandlerMethod(new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod(foo)));showBindMethods(handlerAdapter);log.debug(3. 模拟调用 Controller2 的 bar 方法时 ...);getDataBinderFactory.invoke(handlerAdapter, new HandlerMethod(new WebConfig.Controller2(), WebConfig.Controller2.class.getMethod(bar)));showBindMethods(handlerAdapter);context.close();/*学到了什么a. Method 对象的获取利用了缓存来进行加速b. 绑定器工厂的扩展点(advice 之一), 通过 InitBinder 扩展类型转换器*/}SuppressWarnings(all)private static void showBindMethods(RequestMappingHandlerAdapter handlerAdapter) throws NoSuchFieldException, IllegalAccessException {Field initBinderAdviceCache RequestMappingHandlerAdapter.class.getDeclaredField(initBinderAdviceCache);initBinderAdviceCache.setAccessible(true);MapControllerAdviceBean, SetMethod globalMap (MapControllerAdviceBean, SetMethod) initBinderAdviceCache.get(handlerAdapter);log.debug(全局的 InitBinder 方法 {},globalMap.values().stream().flatMap(ms - ms.stream().map(m - m.getName())).collect(Collectors.toList()));Field initBinderCache RequestMappingHandlerAdapter.class.getDeclaredField(initBinderCache);initBinderCache.setAccessible(true);MapClass?, SetMethod controllerMap (MapClass?, SetMethod) initBinderCache.get(handlerAdapter);log.debug(控制器的 InitBinder 方法 {},controllerMap.entrySet().stream().flatMap(e - e.getValue().stream().map(v - e.getKey().getSimpleName() . v.getName())).collect(Collectors.toList()));} } 收获 RequestMappingHandlerAdapter 初始化时会解析 ControllerAdvice 中的 InitBinder 方法RequestMappingHandlerAdapter 会以类为单位在该类首次使用时解析此类的 InitBinder 方法以上两种 InitBinder 的解析结果都会缓存来避免重复解析控制器方法调用时会综合利用本类的 InitBinder 方法和 ControllerAdvice 中的 InitBinder 方法创建绑定工厂 25) 控制器方法执行流程 图1 #mermaid-svg-M5DrcIDupL0x4sXT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-M5DrcIDupL0x4sXT .error-icon{fill:#552222;}#mermaid-svg-M5DrcIDupL0x4sXT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M5DrcIDupL0x4sXT .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-M5DrcIDupL0x4sXT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M5DrcIDupL0x4sXT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M5DrcIDupL0x4sXT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M5DrcIDupL0x4sXT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M5DrcIDupL0x4sXT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M5DrcIDupL0x4sXT .marker.cross{stroke:#333333;}#mermaid-svg-M5DrcIDupL0x4sXT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M5DrcIDupL0x4sXT g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-M5DrcIDupL0x4sXT g.classGroup text .title{font-weight:bolder;}#mermaid-svg-M5DrcIDupL0x4sXT .nodeLabel,#mermaid-svg-M5DrcIDupL0x4sXT .edgeLabel{color:#131300;}#mermaid-svg-M5DrcIDupL0x4sXT .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-M5DrcIDupL0x4sXT .label text{fill:#131300;}#mermaid-svg-M5DrcIDupL0x4sXT .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-M5DrcIDupL0x4sXT .classTitle{font-weight:bolder;}#mermaid-svg-M5DrcIDupL0x4sXT .node rect,#mermaid-svg-M5DrcIDupL0x4sXT .node circle,#mermaid-svg-M5DrcIDupL0x4sXT .node ellipse,#mermaid-svg-M5DrcIDupL0x4sXT .node polygon,#mermaid-svg-M5DrcIDupL0x4sXT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M5DrcIDupL0x4sXT .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-M5DrcIDupL0x4sXT g.clickable{cursor:pointer;}#mermaid-svg-M5DrcIDupL0x4sXT g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-M5DrcIDupL0x4sXT g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-M5DrcIDupL0x4sXT .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-M5DrcIDupL0x4sXT .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-M5DrcIDupL0x4sXT .dashed-line{stroke-dasharray:3;}#mermaid-svg-M5DrcIDupL0x4sXT #compositionStart,#mermaid-svg-M5DrcIDupL0x4sXT .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #compositionEnd,#mermaid-svg-M5DrcIDupL0x4sXT .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #dependencyStart,#mermaid-svg-M5DrcIDupL0x4sXT .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #dependencyStart,#mermaid-svg-M5DrcIDupL0x4sXT .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #extensionStart,#mermaid-svg-M5DrcIDupL0x4sXT .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #extensionEnd,#mermaid-svg-M5DrcIDupL0x4sXT .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #aggregationStart,#mermaid-svg-M5DrcIDupL0x4sXT .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT #aggregationEnd,#mermaid-svg-M5DrcIDupL0x4sXT .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M5DrcIDupL0x4sXT .edgeTerminals{font-size:11px;}#mermaid-svg-M5DrcIDupL0x4sXT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ServletInvocableHandlerMethod invokeAndHandle(ServletWebRequest,ModelAndViewContainer) HandlerMethod bean method WebDataBinderFactory ParameterNameDiscoverer HandlerMethodArgumentResolverComposite HandlerMethodReturnValueHandlerComposite HandlerMethod 需要 bean 即是哪个 Controllermethod 即是 Controller 中的哪个方法 ServletInvocableHandlerMethod 需要 WebDataBinderFactory 负责对象绑定、类型转换ParameterNameDiscoverer 负责参数名解析HandlerMethodArgumentResolverComposite 负责解析参数HandlerMethodReturnValueHandlerComposite 负责处理返回值 图2 #mermaid-svg-E5OGCS3g3xT1scCM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-E5OGCS3g3xT1scCM .error-icon{fill:#552222;}#mermaid-svg-E5OGCS3g3xT1scCM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E5OGCS3g3xT1scCM .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-E5OGCS3g3xT1scCM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E5OGCS3g3xT1scCM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E5OGCS3g3xT1scCM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E5OGCS3g3xT1scCM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E5OGCS3g3xT1scCM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E5OGCS3g3xT1scCM .marker.cross{stroke:#333333;}#mermaid-svg-E5OGCS3g3xT1scCM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E5OGCS3g3xT1scCM .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-E5OGCS3g3xT1scCM text.actortspan{fill:black;stroke:none;}#mermaid-svg-E5OGCS3g3xT1scCM .actor-line{stroke:grey;}#mermaid-svg-E5OGCS3g3xT1scCM .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-E5OGCS3g3xT1scCM .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-E5OGCS3g3xT1scCM #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-E5OGCS3g3xT1scCM .sequenceNumber{fill:white;}#mermaid-svg-E5OGCS3g3xT1scCM #sequencenumber{fill:#333;}#mermaid-svg-E5OGCS3g3xT1scCM #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-E5OGCS3g3xT1scCM .messageText{fill:#333;stroke:#333;}#mermaid-svg-E5OGCS3g3xT1scCM .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-E5OGCS3g3xT1scCM .labelText,#mermaid-svg-E5OGCS3g3xT1scCM .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-E5OGCS3g3xT1scCM .loopText,#mermaid-svg-E5OGCS3g3xT1scCM .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-E5OGCS3g3xT1scCM .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-E5OGCS3g3xT1scCM .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-E5OGCS3g3xT1scCM .noteText,#mermaid-svg-E5OGCS3g3xT1scCM .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-E5OGCS3g3xT1scCM .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-E5OGCS3g3xT1scCM .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-E5OGCS3g3xT1scCM .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-E5OGCS3g3xT1scCM .actorPopupMenu{position:absolute;}#mermaid-svg-E5OGCS3g3xT1scCM .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-E5OGCS3g3xT1scCM .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-E5OGCS3g3xT1scCM .actor-man circle,#mermaid-svg-E5OGCS3g3xT1scCM line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-E5OGCS3g3xT1scCM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RequestMappingHandlerAdapter WebDataBinderFactory ModelFactory ModelAndViewContainer 准备 InitBinder 准备 ModelAttribute 添加Model数据 RequestMappingHandlerAdapter WebDataBinderFactory ModelFactory ModelAndViewContainer 图3 #mermaid-svg-ZL1f92NSwz2rkcQQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .error-icon{fill:#552222;}#mermaid-svg-ZL1f92NSwz2rkcQQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZL1f92NSwz2rkcQQ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ZL1f92NSwz2rkcQQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZL1f92NSwz2rkcQQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZL1f92NSwz2rkcQQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZL1f92NSwz2rkcQQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZL1f92NSwz2rkcQQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .marker.cross{stroke:#333333;}#mermaid-svg-ZL1f92NSwz2rkcQQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZL1f92NSwz2rkcQQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZL1f92NSwz2rkcQQ text.actortspan{fill:black;stroke:none;}#mermaid-svg-ZL1f92NSwz2rkcQQ .actor-line{stroke:grey;}#mermaid-svg-ZL1f92NSwz2rkcQQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .sequenceNumber{fill:white;}#mermaid-svg-ZL1f92NSwz2rkcQQ #sequencenumber{fill:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .messageText{fill:#333;stroke:#333;}#mermaid-svg-ZL1f92NSwz2rkcQQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZL1f92NSwz2rkcQQ .labelText,#mermaid-svg-ZL1f92NSwz2rkcQQ .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-ZL1f92NSwz2rkcQQ .loopText,#mermaid-svg-ZL1f92NSwz2rkcQQ .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-ZL1f92NSwz2rkcQQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZL1f92NSwz2rkcQQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZL1f92NSwz2rkcQQ .noteText,#mermaid-svg-ZL1f92NSwz2rkcQQ .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-ZL1f92NSwz2rkcQQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZL1f92NSwz2rkcQQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZL1f92NSwz2rkcQQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZL1f92NSwz2rkcQQ .actorPopupMenu{position:absolute;}#mermaid-svg-ZL1f92NSwz2rkcQQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZL1f92NSwz2rkcQQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZL1f92NSwz2rkcQQ .actor-man circle,#mermaid-svg-ZL1f92NSwz2rkcQQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZL1f92NSwz2rkcQQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RequestMappingHandlerAdapter ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer invokeAndHandle 获取 args 有的解析器涉及 RequestBodyAdvice 有的解析器涉及数据绑定生成模型数据 args method.invoke(bean,args) 得到 returnValue 处理 returnValue 有的处理器涉及 ResponseBodyAdvice 添加Model数据,处理视图名,是否渲染等 获取 ModelAndView RequestMappingHandlerAdapter ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 26) ControllerAdvice 之 ModelAttribute 演示 - 准备 ModelAttribute 代码参考 package com.itheima.a26;import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.http.HttpStatus; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.support.DefaultSessionAttributeStore; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.*; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.InvocableHandlerMethod; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.*;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.List;import static com.itheima.a26.WebConfig.*;public class A26 {public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);RequestMappingHandlerAdapter adapter new RequestMappingHandlerAdapter();adapter.setApplicationContext(context);adapter.afterPropertiesSet();MockHttpServletRequest request new MockHttpServletRequest();request.setParameter(name, 张三);/*现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下*/ServletInvocableHandlerMethod handlerMethod new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod(foo, User.class));ServletRequestDataBinderFactory factory new ServletRequestDataBinderFactory(null, null);handlerMethod.setDataBinderFactory(factory);handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));ModelAndViewContainer container new ModelAndViewContainer();// 获取模型工厂方法Method getModelFactory RequestMappingHandlerAdapter.class.getDeclaredMethod(getModelFactory, HandlerMethod.class, WebDataBinderFactory.class);getModelFactory.setAccessible(true);ModelFactory modelFactory (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, factory);// 初始化模型数据modelFactory.initModel(new ServletWebRequest(request), container, handlerMethod);handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);System.out.println(container.getModel());context.close();/*学到了什么a. 控制器方法是如何调用的b. 模型数据如何产生c. advice 之二, ModelAttribute 补充模型数据*/}public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {HandlerMethodArgumentResolverComposite composite new HandlerMethodArgumentResolverComposite();composite.addResolvers(new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),new PathVariableMethodArgumentResolver(),new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ServletRequestMethodArgumentResolver(),new ServletModelAttributeMethodProcessor(false),new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),new ServletModelAttributeMethodProcessor(true),new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true));return composite;}} package com.itheima.a26;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;Configuration public class WebConfig {ControllerAdvicestatic class MyControllerAdvice {ModelAttribute(a)public String aa() {return aa;}}Controllerstatic class Controller1 {ModelAttribute(b)public String aa() {return bb;}ResponseStatus(HttpStatus.OK)public ModelAndView foo(ModelAttribute(u) User user) {System.out.println(foo);return null;}}static class User {private String name;public void setName(String name) {this.name name;}public String getName() {return name;}Overridepublic String toString() {return User{ name name \ };}} } 准备 ModelAttribute 在整个 HandlerAdapter 调用过程中所处的位置 #mermaid-svg-URJjeQnv40n5x6nP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-URJjeQnv40n5x6nP .error-icon{fill:#552222;}#mermaid-svg-URJjeQnv40n5x6nP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-URJjeQnv40n5x6nP .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-URJjeQnv40n5x6nP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-URJjeQnv40n5x6nP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-URJjeQnv40n5x6nP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-URJjeQnv40n5x6nP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-URJjeQnv40n5x6nP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-URJjeQnv40n5x6nP .marker.cross{stroke:#333333;}#mermaid-svg-URJjeQnv40n5x6nP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-URJjeQnv40n5x6nP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-URJjeQnv40n5x6nP text.actortspan{fill:black;stroke:none;}#mermaid-svg-URJjeQnv40n5x6nP .actor-line{stroke:grey;}#mermaid-svg-URJjeQnv40n5x6nP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-URJjeQnv40n5x6nP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-URJjeQnv40n5x6nP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-URJjeQnv40n5x6nP .sequenceNumber{fill:white;}#mermaid-svg-URJjeQnv40n5x6nP #sequencenumber{fill:#333;}#mermaid-svg-URJjeQnv40n5x6nP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-URJjeQnv40n5x6nP .messageText{fill:#333;stroke:#333;}#mermaid-svg-URJjeQnv40n5x6nP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-URJjeQnv40n5x6nP .labelText,#mermaid-svg-URJjeQnv40n5x6nP .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-URJjeQnv40n5x6nP .loopText,#mermaid-svg-URJjeQnv40n5x6nP .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-URJjeQnv40n5x6nP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-URJjeQnv40n5x6nP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-URJjeQnv40n5x6nP .noteText,#mermaid-svg-URJjeQnv40n5x6nP .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-URJjeQnv40n5x6nP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-URJjeQnv40n5x6nP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-URJjeQnv40n5x6nP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-URJjeQnv40n5x6nP .actorPopupMenu{position:absolute;}#mermaid-svg-URJjeQnv40n5x6nP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-URJjeQnv40n5x6nP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-URJjeQnv40n5x6nP .actor-man circle,#mermaid-svg-URJjeQnv40n5x6nP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-URJjeQnv40n5x6nP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 准备 InitBinder 准备 ModelAttribute 添加Model数据 invokeAndHandle 获取 args 有的解析器涉及 RequestBodyAdvice 有的解析器涉及数据绑定生成Model数据 args method.invoke(bean,args) 得到 returnValue 处理 returnValue 有的处理器涉及 ResponseBodyAdvice 添加Model数据,处理视图名,是否渲染等 获取 ModelAndView HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 收获 RequestMappingHandlerAdapter 初始化时会解析 ControllerAdvice 中的 ModelAttribute 方法RequestMappingHandlerAdapter 会以类为单位在该类首次使用时解析此类的 ModelAttribute 方法以上两种 ModelAttribute 的解析结果都会缓存来避免重复解析控制器方法调用时会综合利用本类的 ModelAttribute 方法和 ControllerAdvice 中的 ModelAttribute 方法创建模型工厂 27) 返回值处理器 演示 - 常见返回值处理器 代码参考 package com.itheima.a27;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import org.springframework.web.servlet.mvc.method.annotation.*; import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator; import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; import org.springframework.web.util.UrlPathHelper;import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Locale;/*目标: 解析控制器方法的返回值常见的返回值处理器org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler4c9e38org.springframework.web.method.annotation.ModelMethodProcessor5d1e09bcorg.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler4bdc8b5dorg.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler3bcd426corg.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler5f14a673org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor726a17c4org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler5dc3fcb7org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandlerc4c0b41org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler76911385org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler5467eea4org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor160396dborg.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor7a799159org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler40ab8a8org.springframework.web.method.annotation.MapMethodProcessor6ff37443org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor65cc8228*/ public class A27 {private static final Logger log LoggerFactory.getLogger(A27.class);public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);// 1. 测试返回值类型为 ModelAndView// 2. 测试返回值类型为 String 时, 把它当做视图名// 3. 测试返回值添加了 ModelAttribute 注解时, 此时需找到默认视图名// 4. 测试返回值不加 ModelAttribute 注解且返回非简单类型时, 此时需找到默认视图名// 5. 测试返回值类型为 ResponseEntity 时, 此时不走视图流程// 6. 测试返回值类型为 HttpHeaders 时, 此时不走视图流程// 7. 测试返回值添加了 ResponseBody 注解时, 此时不走视图流程test7(context);/*学到了什么a. 每个返回值处理器能干啥1) 看是否支持某种返回值2) 返回值或作为模型、或作为视图名、或作为响应体 ...b. 组合模式在 Spring 中的体现 1*/}private static void test7(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test7);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();ServletWebRequest webRequest new ServletWebRequest(request, response);if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());if (!container.isRequestHandled()) {renderView(context, container, webRequest); // 渲染视图} else {for (String name : response.getHeaderNames()) {System.out.println(name response.getHeader(name));}System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}}}private static void test6(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test6);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();ServletWebRequest webRequest new ServletWebRequest(request, response);if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());if (!container.isRequestHandled()) {renderView(context, container, webRequest); // 渲染视图} else {for (String name : response.getHeaderNames()) {System.out.println(name response.getHeader(name));}System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}}}private static void test5(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test5);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();ServletWebRequest webRequest new ServletWebRequest(request, response);if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());if (!container.isRequestHandled()) {renderView(context, container, webRequest); // 渲染视图} else {System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}}}private static void test4(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test4);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();MockHttpServletRequest request new MockHttpServletRequest();request.setRequestURI(/test4);UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);ServletWebRequest webRequest new ServletWebRequest(request, new MockHttpServletResponse());if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());renderView(context, container, webRequest); // 渲染视图}}private static void test3(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test3);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();MockHttpServletRequest request new MockHttpServletRequest();request.setRequestURI(/test3);UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);ServletWebRequest webRequest new ServletWebRequest(request, new MockHttpServletResponse());if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());renderView(context, container, webRequest); // 渲染视图}}private static void test2(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test2);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();ServletWebRequest webRequest new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());renderView(context, container, webRequest); // 渲染视图}}private static void test1(AnnotationConfigApplicationContext context) throws Exception {Method method Controller.class.getMethod(test1);Controller controller new Controller();Object returnValue method.invoke(controller); // 获取返回值HandlerMethod methodHandle new HandlerMethod(controller, method);ModelAndViewContainer container new ModelAndViewContainer();HandlerMethodReturnValueHandlerComposite composite getReturnValueHandler();ServletWebRequest webRequest new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);System.out.println(container.getModel());System.out.println(container.getViewName());renderView(context, container, webRequest); // 渲染视图}}public static HandlerMethodReturnValueHandlerComposite getReturnValueHandler() {HandlerMethodReturnValueHandlerComposite composite new HandlerMethodReturnValueHandlerComposite();composite.addHandler(new ModelAndViewMethodReturnValueHandler());composite.addHandler(new ViewNameMethodReturnValueHandler());composite.addHandler(new ServletModelAttributeMethodProcessor(false));composite.addHandler(new HttpEntityMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())));composite.addHandler(new HttpHeadersReturnValueHandler());composite.addHandler(new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())));composite.addHandler(new ServletModelAttributeMethodProcessor(true));return composite;}SuppressWarnings(all)private static void renderView(AnnotationConfigApplicationContext context, ModelAndViewContainer container,ServletWebRequest webRequest) throws Exception {log.debug( 渲染视图);FreeMarkerViewResolver resolver context.getBean(FreeMarkerViewResolver.class);String viewName container.getViewName() ! null ? container.getViewName() : new DefaultRequestToViewNameTranslator().getViewName(webRequest.getRequest());log.debug(没有获取到视图名, 采用默认视图名: {}, viewName);// 每次渲染时, 会产生新的视图对象, 它并非被 Spring 所管理, 但确实借助了 Spring 容器来执行初始化View view resolver.resolveViewName(viewName, Locale.getDefault());view.render(container.getModel(), webRequest.getRequest(), webRequest.getResponse());System.out.println(new String(((MockHttpServletResponse) webRequest.getResponse()).getContentAsByteArray(), StandardCharsets.UTF_8));}static class Controller {private static final Logger log LoggerFactory.getLogger(Controller.class);public ModelAndView test1() {log.debug(test1());ModelAndView mav new ModelAndView(view1);mav.addObject(name, 张三);return mav;}public String test2() {log.debug(test2());return view2;}ModelAttribute // RequestMapping(/test3)public User test3() {log.debug(test3());return new User(李四, 20);}public User test4() {log.debug(test4());return new User(王五, 30);}public HttpEntityUser test5() {log.debug(test5());return new HttpEntity(new User(赵六, 40));}public HttpHeaders test6() {log.debug(test6());HttpHeaders headers new HttpHeaders();headers.add(Content-Type, text/html);return headers;}ResponseBodypublic User test7() {log.debug(test7());return new User(钱七, 50);}}// 必须用 public 修饰, 否则 freemarker 渲染其 name, age 属性时失败public static class User {private String name;private int age;public User(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}Overridepublic String toString() {return User{ name name \ , age age };}} } package com.itheima.a27;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.view.AbstractUrlBasedView; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import org.springframework.web.servlet.view.freemarker.FreeMarkerView; import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;Configuration public class WebConfig {Beanpublic FreeMarkerConfigurer freeMarkerConfigurer() {FreeMarkerConfigurer configurer new FreeMarkerConfigurer();configurer.setDefaultEncoding(utf-8);configurer.setTemplateLoaderPath(classpath:templates);return configurer;}Bean // FreeMarkerView 在借助 Spring 初始化时会要求 web 环境才会走 setConfiguration, 这里想办法去掉了 web 环境的约束public FreeMarkerViewResolver viewResolver(FreeMarkerConfigurer configurer) {FreeMarkerViewResolver resolver new FreeMarkerViewResolver() {Overrideprotected AbstractUrlBasedView instantiateView() {FreeMarkerView view new FreeMarkerView() {Overrideprotected boolean isContextRequired() {return false;}};view.setConfiguration(configurer.getConfiguration());return view;}};resolver.setContentType(text/html;charsetutf-8);resolver.setPrefix(/);resolver.setSuffix(.ftl);resolver.setExposeSpringMacroHelpers(false);return resolver;} } 收获 常见的返回值处理器 ModelAndView分别获取其模型和视图名放入 ModelAndViewContainer返回值类型为 String 时把它当做视图名放入 ModelAndViewContainer返回值添加了 ModelAttribute 注解时将返回值作为模型放入 ModelAndViewContainer 此时需找到默认视图名 返回值省略 ModelAttribute 注解且返回非简单类型时将返回值作为模型放入 ModelAndViewContainer 此时需找到默认视图名 返回值类型为 ResponseEntity 时 此时走 MessageConverter并设置 ModelAndViewContainer.requestHandled 为 true 返回值类型为 HttpHeaders 时 会设置 ModelAndViewContainer.requestHandled 为 true 返回值添加了 ResponseBody 注解时 此时走 MessageConverter并设置 ModelAndViewContainer.requestHandled 为 true 组合模式在 Spring 中的体现 1 28) MessageConverter 演示 - MessageConverter 的作用 代码参考 package com.itheima.a28;import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.mock.http.MockHttpInputMessage; import org.springframework.mock.http.MockHttpOutputMessage; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List;public class A28 {public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException { // test1(); // test2(); // test3();test4();/*学到了什么a. MessageConverter 的作用, ResponseBody 是返回值处理器解析的, 但具体转换工作是 MessageConverter 做的b. 如何选择 MediaType- 首先看 RequestMapping 上有没有指定- 其次看 request 的 Accept 头有没有指定- 最后按 MessageConverter 的顺序, 谁能谁先转换*/}private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();ServletWebRequest webRequest new ServletWebRequest(request, response);request.addHeader(Accept, application/xml);response.setContentType(application/json);RequestResponseBodyMethodProcessor processor new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()));processor.handleReturnValue(new User(张三, 18),new MethodParameter(A28.class.getMethod(user), -1),new ModelAndViewContainer(),webRequest);System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}ResponseBodyRequestMapping(produces application/json)public User user() {return null;}private static void test3() throws IOException {MockHttpInputMessage message new MockHttpInputMessage({name:李四,age:20}.getBytes(StandardCharsets.UTF_8));MappingJackson2HttpMessageConverter converter new MappingJackson2HttpMessageConverter();if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {Object read converter.read(User.class, message);System.out.println(read);}}private static void test2() throws IOException {MockHttpOutputMessage message new MockHttpOutputMessage();MappingJackson2XmlHttpMessageConverter converter new MappingJackson2XmlHttpMessageConverter();if (converter.canWrite(User.class, MediaType.APPLICATION_XML)) {converter.write(new User(李四, 20), MediaType.APPLICATION_XML, message);System.out.println(message.getBodyAsString());}}public static void test1() throws IOException {MockHttpOutputMessage message new MockHttpOutputMessage();MappingJackson2HttpMessageConverter converter new MappingJackson2HttpMessageConverter();if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {converter.write(new User(张三, 18), MediaType.APPLICATION_JSON, message);System.out.println(message.getBodyAsString());}}public static class User {private String name;private int age;JsonCreatorpublic User(JsonProperty(name) String name, JsonProperty(age) int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}Overridepublic String toString() {return User{ name name \ , age age };}} } 收获 MessageConverter 的作用 ResponseBody 是返回值处理器解析的但具体转换工作是 MessageConverter 做的 如何选择 MediaType 首先看 RequestMapping 上有没有指定其次看 request 的 Accept 头有没有指定最后按 MessageConverter 的顺序, 谁能谁先转换 29) ControllerAdvice 之 ResponseBodyAdvice 演示 - ResponseBodyAdvice 增强 代码参考 package com.itheima.a29;import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.ControllerAdviceBean; import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver; import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver; import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.*;import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.stream.Collectors;public class A29 {// {name:王五,age:18}// {code:xx, msg:xx, data: {name:王五,age:18} }public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);ServletInvocableHandlerMethod handlerMethod new ServletInvocableHandlerMethod(context.getBean(WebConfig.MyController.class),WebConfig.MyController.class.getMethod(user));handlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(Collections.emptyList(), null));handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));handlerMethod.setHandlerMethodReturnValueHandlers(getReturnValueHandlers(context));MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();ModelAndViewContainer container new ModelAndViewContainer();handlerMethod.invokeAndHandle(new ServletWebRequest(request, response), container);System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));/*学到了什么a. advice 之三, ResponseBodyAdvice 返回响应体前包装*/}public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {HandlerMethodArgumentResolverComposite composite new HandlerMethodArgumentResolverComposite();composite.addResolvers(new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),new PathVariableMethodArgumentResolver(),new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),new ServletRequestMethodArgumentResolver(),new ServletModelAttributeMethodProcessor(false),new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),new ServletModelAttributeMethodProcessor(true),new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true));return composite;}public static HandlerMethodReturnValueHandlerComposite getReturnValueHandlers(AnnotationConfigApplicationContext context) {// 添加 adviceListControllerAdviceBean annotatedBeans ControllerAdviceBean.findAnnotatedBeans(context);ListObject collect annotatedBeans.stream().filter(b - ResponseBodyAdvice.class.isAssignableFrom(b.getBeanType())).collect(Collectors.toList());HandlerMethodReturnValueHandlerComposite composite new HandlerMethodReturnValueHandlerComposite();composite.addHandler(new ModelAndViewMethodReturnValueHandler());composite.addHandler(new ViewNameMethodReturnValueHandler());composite.addHandler(new ServletModelAttributeMethodProcessor(false));composite.addHandler(new HttpEntityMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())));composite.addHandler(new HttpHeadersReturnValueHandler());composite.addHandler(new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter()), collect));composite.addHandler(new ServletModelAttributeMethodProcessor(true));return composite;} } package com.itheima.a29;import org.springframework.context.annotation.Configuration; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;Configuration public class WebConfig {ControllerAdvicestatic class MyControllerAdvice implements ResponseBodyAdviceObject {// 满足条件才转换public boolean supports(MethodParameter returnType, Class? extends HttpMessageConverter? converterType) {if (returnType.getMethodAnnotation(ResponseBody.class) ! null ||AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) ! null) { // returnType.getContainingClass().isAnnotationPresent(ResponseBody.class)) {return true;}return false;}// 将 User 或其它类型统一为 Result 类型public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class? extends HttpMessageConverter? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof Result) {return body;}return Result.ok(body);}}// Controller// ResponseBodyRestControllerpublic static class MyController {public User user() {return new User(王五, 18);}}public static class User {private String name;private int age;public User(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}} } ResponseBodyAdvice 增强 在整个 HandlerAdapter 调用过程中所处的位置 #mermaid-svg-Q5U0MZ89ynI3sFpo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .error-icon{fill:#552222;}#mermaid-svg-Q5U0MZ89ynI3sFpo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q5U0MZ89ynI3sFpo .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Q5U0MZ89ynI3sFpo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q5U0MZ89ynI3sFpo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q5U0MZ89ynI3sFpo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q5U0MZ89ynI3sFpo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q5U0MZ89ynI3sFpo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .marker.cross{stroke:#333333;}#mermaid-svg-Q5U0MZ89ynI3sFpo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q5U0MZ89ynI3sFpo .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q5U0MZ89ynI3sFpo text.actortspan{fill:black;stroke:none;}#mermaid-svg-Q5U0MZ89ynI3sFpo .actor-line{stroke:grey;}#mermaid-svg-Q5U0MZ89ynI3sFpo .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .sequenceNumber{fill:white;}#mermaid-svg-Q5U0MZ89ynI3sFpo #sequencenumber{fill:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .messageText{fill:#333;stroke:#333;}#mermaid-svg-Q5U0MZ89ynI3sFpo .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q5U0MZ89ynI3sFpo .labelText,#mermaid-svg-Q5U0MZ89ynI3sFpo .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-Q5U0MZ89ynI3sFpo .loopText,#mermaid-svg-Q5U0MZ89ynI3sFpo .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-Q5U0MZ89ynI3sFpo .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Q5U0MZ89ynI3sFpo .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Q5U0MZ89ynI3sFpo .noteText,#mermaid-svg-Q5U0MZ89ynI3sFpo .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-Q5U0MZ89ynI3sFpo .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q5U0MZ89ynI3sFpo .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q5U0MZ89ynI3sFpo .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q5U0MZ89ynI3sFpo .actorPopupMenu{position:absolute;}#mermaid-svg-Q5U0MZ89ynI3sFpo .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Q5U0MZ89ynI3sFpo .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q5U0MZ89ynI3sFpo .actor-man circle,#mermaid-svg-Q5U0MZ89ynI3sFpo line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Q5U0MZ89ynI3sFpo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 准备 InitBinder 准备 ModelAttribute 添加Model数据 invokeAndHandle 获取 args 有的解析器涉及 RequestBodyAdvice 有的解析器涉及数据绑定生成Model数据 args method.invoke(bean,args) 得到 returnValue 处理 returnValue 有的处理器涉及 ResponseBodyAdvice 添加Model数据,处理视图名,是否渲染等 获取 ModelAndView HandlerAdapter WebDataBinderFactory ModelFactory ServletInvocableHandlerMethod ArgumentResolvers ReturnValueHandlers ModelAndViewContainer 收获 ResponseBodyAdvice 返回响应体前包装 30) 异常解析器 演示 - ExceptionHandlerExceptionResolver 代码参考 package com.itheima.a30;import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.List; import java.util.Map;public class A30 {public static void main(String[] args) throws NoSuchMethodException {ExceptionHandlerExceptionResolver resolver new ExceptionHandlerExceptionResolver();resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));resolver.afterPropertiesSet();MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();// 1.测试 json // HandlerMethod handlerMethod new HandlerMethod(new Controller1(), Controller1.class.getMethod(foo)); // Exception e new ArithmeticException(被零除); // resolver.resolveException(request, response, handlerMethod, e); // System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));// 2.测试 mav // HandlerMethod handlerMethod new HandlerMethod(new Controller2(), Controller2.class.getMethod(foo)); // Exception e new ArithmeticException(被零除); // ModelAndView mav resolver.resolveException(request, response, handlerMethod, e); // System.out.println(mav.getModel()); // System.out.println(mav.getViewName());// 3.测试嵌套异常 // HandlerMethod handlerMethod new HandlerMethod(new Controller3(), Controller3.class.getMethod(foo)); // Exception e new Exception(e1, new RuntimeException(e2, new IOException(e3))); // resolver.resolveException(request, response, handlerMethod, e); // System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));// 4.测试异常处理方法参数解析HandlerMethod handlerMethod new HandlerMethod(new Controller4(), Controller4.class.getMethod(foo));Exception e new Exception(e1);resolver.resolveException(request, response, handlerMethod, e);System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));/*学到了什么a. ExceptionHandlerExceptionResolver 能够重用参数解析器、返回值处理器实现组件重用b. 能够支持嵌套异常*/}static class Controller1 {public void foo() {}ExceptionHandlerResponseBodypublic MapString, Object handle(ArithmeticException e) {return Map.of(error, e.getMessage());}}static class Controller2 {public void foo() {}ExceptionHandlerpublic ModelAndView handle(ArithmeticException e) {return new ModelAndView(test2, Map.of(error, e.getMessage()));}}static class Controller3 {public void foo() {}ExceptionHandlerResponseBodypublic MapString, Object handle(IOException e3) {return Map.of(error, e3.getMessage());}}static class Controller4 {public void foo() {}ExceptionHandlerResponseBodypublic MapString, Object handler(Exception e, HttpServletRequest request) {System.out.println(request);return Map.of(error, e.getMessage());}} } 收获 它能够重用参数解析器、返回值处理器实现组件重用它能够支持嵌套异常 31) ControllerAdvice 之 ExceptionHandler 演示 - 准备 ExceptionHandler 代码参考 package com.itheima.a31;import com.itheima.a30.A30; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map;public class A31 {public static void main(String[] args) throws NoSuchMethodException {MockHttpServletRequest request new MockHttpServletRequest();MockHttpServletResponse response new MockHttpServletResponse();// ExceptionHandlerExceptionResolver resolver new ExceptionHandlerExceptionResolver(); // resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter())); // resolver.afterPropertiesSet();AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(WebConfig.class);ExceptionHandlerExceptionResolver resolver context.getBean(ExceptionHandlerExceptionResolver.class);HandlerMethod handlerMethod new HandlerMethod(new Controller5(), Controller5.class.getMethod(foo));Exception e new Exception(e1);resolver.resolveException(request, response, handlerMethod, e);System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}static class Controller5 {public void foo() {}} } package com.itheima.a31;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;import java.util.List; import java.util.Map;Configuration public class WebConfig {ControllerAdvicestatic class MyControllerAdvice {ExceptionHandlerResponseBodypublic MapString, Object handle(Exception e) {return Map.of(error, e.getMessage());}}Beanpublic ExceptionHandlerExceptionResolver resolver() {ExceptionHandlerExceptionResolver resolver new ExceptionHandlerExceptionResolver();resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));return resolver;} } 收获 ExceptionHandlerExceptionResolver 初始化时会解析 ControllerAdvice 中的 ExceptionHandler 方法ExceptionHandlerExceptionResolver 会以类为单位在该类首次处理异常时解析此类的 ExceptionHandler 方法以上两种 ExceptionHandler 的解析结果都会缓存来避免重复解析 32) Tomcat 异常处理 我们知道 ExceptionHandler 只能处理发生在 mvc 流程中的异常例如控制器内、拦截器内那么如果是 Filter 出现了异常如何进行处理呢 在 Spring Boot 中是这么实现的 因为内嵌了 Tomcat 容器因此可以配置 Tomcat 的错误页面Filter 与 错误页面之间是通过请求转发跳转的可以在这里做手脚先通过 ErrorPageRegistrarBeanPostProcessor 这个后处理器配置错误页面地址默认为 /error 也可以通过 ${server.error.path} 进行配置当 Filter 发生异常时不会走 Spring 流程但会走 Tomcat 的错误处理于是就希望转发至 /error 这个地址 当然如果没有 ExceptionHandler那么最终也会走到 Tomcat 的错误处理 Spring Boot 又提供了一个 BasicErrorController它就是一个标准 ControllerRequestMapping 配置为 /error所以处理异常的职责就又回到了 Spring异常信息由于会被 Tomcat 放入 request 作用域因此 BasicErrorController 里也能获取到具体异常信息会由 DefaultErrorAttributes 封装好BasicErrorController 通过 Accept 头判断需要生成哪种 MediaType 的响应 如果要的不是 text/html走 MessageConverter 流程如果需要 text/html走 mvc 流程此时又分两种情况 配置了 ErrorViewResolver根据状态码去找 View没配置或没找到用 BeanNameViewResolver 根据一个固定为 error 的名字找到 View即所谓的 WhitelabelErrorView 评价 一个错误处理搞得这么复杂就问恶心不 演示1 - 错误页处理 关键代码 Bean // ⬅️修改了 Tomcat 服务器默认错误地址, 出错时使用请求转发方式跳转 public ErrorPageRegistrar errorPageRegistrar() {return webServerFactory - webServerFactory.addErrorPages(new ErrorPage(/error)); }Bean // ⬅️TomcatServletWebServerFactory 初始化前用它增强, 注册所有 ErrorPageRegistrar public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor() {return new ErrorPageRegistrarBeanPostProcessor(); }收获 Tomcat 的错误页处理手段 演示2 - BasicErrorController 关键代码 Bean // ⬅️ErrorProperties 封装环境键值, ErrorAttributes 控制有哪些错误信息 public BasicErrorController basicErrorController() {ErrorProperties errorProperties new ErrorProperties();errorProperties.setIncludeException(true);return new BasicErrorController(new DefaultErrorAttributes(), errorProperties); }Bean // ⬅️名称为 error 的视图, 作为 BasicErrorController 的 text/html 响应结果 public View error() {return new View() {Overridepublic void render(MapString, ? model, HttpServletRequest request, HttpServletResponse response) throws Exception {System.out.println(model);response.setContentType(text/html;charsetutf-8);response.getWriter().print(h3服务器内部错误/h3);}}; }Bean // ⬅️收集容器中所有 View 对象, bean 的名字作为视图名 public ViewResolver viewResolver() {return new BeanNameViewResolver(); }收获 Spring Boot 中 BasicErrorController 如何工作 33) BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter 演示 - 本组映射器和适配器 关键代码 Bean public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {return new BeanNameUrlHandlerMapping(); }Bean public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {return new SimpleControllerHandlerAdapter(); }Bean(/c3) public Controller controller3() {return (request, response) - {response.getWriter().print(this is c3);return null;}; }收获 BeanNameUrlHandlerMapping以 / 开头的 bean 的名字会被当作映射路径这些 bean 本身当作 handler要求实现 Controller 接口SimpleControllerHandlerAdapter调用 handler模拟实现这组映射器和适配器 34) RouterFunctionMapping 与 HandlerFunctionAdapter 演示 - 本组映射器和适配器 关键代码 Bean public RouterFunctionMapping routerFunctionMapping() {return new RouterFunctionMapping(); }Bean public HandlerFunctionAdapter handlerFunctionAdapter() {return new HandlerFunctionAdapter(); }Bean public RouterFunctionServerResponse r1() {// ⬇️映射条件 ⬇️handlerreturn route(GET(/r1), request - ok().body(this is r1)); }收获 RouterFunctionMapping, 通过 RequestPredicate 条件映射handler 要实现 HandlerFunction 接口HandlerFunctionAdapter, 调用 handler 35) SimpleUrlHandlerMapping 与 HttpRequestHandlerAdapter 演示1 - 本组映射器和适配器 代码参考 org.springframework.boot.autoconfigure.web.servlet.A35 关键代码 Bean public SimpleUrlHandlerMapping simpleUrlHandlerMapping(ApplicationContext context) {SimpleUrlHandlerMapping handlerMapping new SimpleUrlHandlerMapping();MapString, ResourceHttpRequestHandler map context.getBeansOfType(ResourceHttpRequestHandler.class);handlerMapping.setUrlMap(map);return handlerMapping; }Bean public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {return new HttpRequestHandlerAdapter(); }Bean(/**) public ResourceHttpRequestHandler handler1() {ResourceHttpRequestHandler handler new ResourceHttpRequestHandler();handler.setLocations(List.of(new ClassPathResource(static/)));return handler; }Bean(/img/**) public ResourceHttpRequestHandler handler2() {ResourceHttpRequestHandler handler new ResourceHttpRequestHandler();handler.setLocations(List.of(new ClassPathResource(images/)));return handler; }收获 SimpleUrlHandlerMapping 不会在初始化时收集映射信息需要手动收集SimpleUrlHandlerMapping 映射路径ResourceHttpRequestHandler 作为静态资源 handlerHttpRequestHandlerAdapter, 调用此 handler 演示2 - 静态资源解析优化 关键代码 Bean(/**) public ResourceHttpRequestHandler handler1() {ResourceHttpRequestHandler handler new ResourceHttpRequestHandler();handler.setLocations(List.of(new ClassPathResource(static/)));handler.setResourceResolvers(List.of(// ⬇️缓存优化new CachingResourceResolver(new ConcurrentMapCache(cache1)),// ⬇️压缩优化new EncodedResourceResolver(),// ⬇️原始资源解析new PathResourceResolver()));return handler; }收获 责任链模式体现压缩文件需要手动生成 演示3 - 欢迎页 关键代码 Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext context) {Resource resource context.getResource(classpath:static/index.html);return new WelcomePageHandlerMapping(null, context, resource, /**); }Bean public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {return new SimpleControllerHandlerAdapter(); }收获 欢迎页支持静态欢迎页与动态欢迎页WelcomePageHandlerMapping 映射欢迎页即只映射 ‘/’ 它内置的 handler ParameterizableViewController 作用是不执行逻辑仅根据视图名找视图视图名固定为 forward:index.html SimpleControllerHandlerAdapter, 调用 handler 转发至 /index.html处理 /index.html 又会走上面的静态资源处理流程 映射器与适配器小结 HandlerMapping 负责建立请求与控制器之间的映射关系 RequestMappingHandlerMapping (与 RequestMapping 匹配)WelcomePageHandlerMapping (/)BeanNameUrlHandlerMapping (与 bean 的名字匹配 以 / 开头)RouterFunctionMapping (函数式 RequestPredicate, HandlerFunction)SimpleUrlHandlerMapping (静态资源 通配符 /** /img/**)之间也会有顺序问题, boot 中默认顺序如上 HandlerAdapter 负责实现对各种各样的 handler 的适配调用 RequestMappingHandlerAdapter 处理RequestMapping 方法 参数解析器、返回值处理器体现了组合模式 SimpleControllerHandlerAdapter 处理Controller 接口HandlerFunctionAdapter 处理HandlerFunction 函数式接口HttpRequestHandlerAdapter 处理HttpRequestHandler 接口 (静态资源处理)这也是典型适配器模式体现 36) mvc 处理流程 当浏览器发送一个请求 http://localhost:8080/hello 后请求到达服务器其处理流程是 服务器提供了 DispatcherServlet它使用的是标准 Servlet 技术 路径默认映射路径为 /即会匹配到所有请求 URL可作为请求的统一入口也被称之为前控制器 jsp 不会匹配到 DispatcherServlet其它有路径的 Servlet 匹配优先级也高于 DispatcherServlet 创建在 Boot 中由 DispatcherServletAutoConfiguration 这个自动配置类提供 DispatcherServlet 的 bean初始化DispatcherServlet 初始化时会优先到容器里寻找各种组件作为它的成员变量 HandlerMapping初始化时记录映射关系HandlerAdapter初始化时准备参数解析器、返回值处理器、消息转换器HandlerExceptionResolver初始化时准备参数解析器、返回值处理器、消息转换器ViewResolver DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法 例如根据 /hello 路径找到 RequestMapping(“/hello”) 对应的控制器方法 控制器方法会被封装为 HandlerMethod 对象并结合匹配到的拦截器一起返回给 DispatcherServlet HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain调用链对象 DispatcherServlet 接下来会 调用拦截器的 preHandle 方法RequestMappingHandlerAdapter 调用 handle 方法准备数据绑定工厂、模型工厂、ModelAndViewContainer、将 HandlerMethod 完善为 ServletInvocableHandlerMethod ControllerAdvice 全局增强点1️⃣补充模型数据ControllerAdvice 全局增强点2️⃣补充自定义类型转换器使用 HandlerMethodArgumentResolver 准备参数 ControllerAdvice 全局增强点3️⃣RequestBody 增强 调用 ServletInvocableHandlerMethod使用 HandlerMethodReturnValueHandler 处理返回值 ControllerAdvice 全局增强点4️⃣ResponseBody 增强 根据 ModelAndViewContainer 获取 ModelAndView 如果返回的 ModelAndView 为 null不走第 4 步视图解析及渲染流程 例如有的返回值处理器调用了 HttpMessageConverter 来将结果转换为 JSON这时 ModelAndView 就为 null 如果返回的 ModelAndView 不为 null会在第 4 步走视图解析及渲染流程 调用拦截器的 postHandle 方法处理异常或视图渲染 如果 1~3 出现异常走 ExceptionHandlerExceptionResolver 处理异常流程 ControllerAdvice 全局增强点5️⃣ExceptionHandler 异常处理 正常走视图解析及渲染流程 调用拦截器的 afterCompletion 方法
http://www.dnsts.com.cn/news/107216.html

相关文章:

  • 官网网站建设平台免费网页设计成品下载
  • 网站建设东莞长安镇北京网站备案拍照
  • 电子商务网站建设哪好中国企业查询平台
  • 用jsp做学校网站中卫网站推广优化公司
  • 餐饮网站建设设计asp.net企业网站
  • 网站开发课程的心得网站建设需要多少时间
  • 做网站需不需要服务器wordpress 转nodejs
  • 汶上网站建设哪家好网站开发公司怎么查
  • 免费搭建微信网站设计网站设计公司天津
  • 萍缘网站建设工作wordpress主题缩略图
  • 自建外贸网站多少钱网站研发进度表下载
  • 十大免费跨境网站北京软件app开发
  • 做复印机的模板网站虚拟主机能做什么
  • 陕西最好的云营销网站建设公司怎么样开发app软件
  • 网站建立公司四川龙口市规划建设局网站
  • 商丘网站制作公司一二三网络推广十堰做网站最好的公司
  • 网站建设需要多少内存展馆网站建设方案
  • 做网页赚钱的网站重庆建设企业网站
  • 做网站要钱么网站建设与运营成本
  • 网站空间和云服务器辽宁平台网站建设公司
  • 网站建设的步骤和要点宁波小程序网络开发公司
  • 怎么做触屏版网站建设银行采购网站
  • 网站开发好了 怎么发布免费软件 全免费
  • 邯郸网站建设开发公司吉安工商注册官方网站
  • seo网站推广优化论文各大网站rss地址
  • 广东微信网站制作报价表四大门户网站创始人
  • 借贷网站建设一个网站能放多少关键词
  • 湖南网站建设小公司坤和建设 网站
  • 怎么在传奇网站上做宣传邯郸资讯
  • 网站建设如何为企业电商化转型赋能手机网页代码