详解SpringMVC

1.DispatcherServlet初始化时机

  • DispatcherServlet是由spring创建的,初始化是由Tomcat完成的,通过setLoadOnStartup来决定是否为tomcat启动时初始化
@Configuration
@ComponentScan // 没有设置扫描包的话默认扫描当前配置的包及其子包
@PropertySource("classpath:application.properties") // 指定资源文件读取的位置
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) //使 @ConfigurationProperties 注解生效,并且将组件加入 IOC 容器中
public class WebConfig {

    // 内嵌web容器工厂
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {
        TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        serverFactory.setPort(serverProperties.getPort());
        return serverFactory;
    }

    // 创建DispatcherServlet
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    // 注册DispatcherServlet到tomcat
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet,
                                                              WebMvcProperties webMvcProperties) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        // 1=tomcat启动时DispatcherServlet初始化
        // -1=第一次请求到时候初始化
        registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
        return registrationBean;
    }
}

public class A10Application {
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        context.registerBean(WebConfig.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

    }
}

server.port=8080
spring.mvc.servlet.load-on-startup=1

详解SpringMVC_第1张图片

2.DispatcherServlet初始化

   protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);   // 初始化文件上传解析器
        this.initLocaleResolver(context);			// 初始化本地语言解析器 (中文、英文...)
        this.initThemeResolver(context);
        this.initHandlerMappings(context);    // 初始化映射处理器
        this.initHandlerAdapters(context);    // 初始化适配处理器
        this.initHandlerExceptionResolvers(context);  // 初始化异常解析处理器
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }
  • RequestMappingHandlerMapping用途

       /*
         如果使用DispatcherServlet的默认RequestMappingHandlerMapping,从容器中是获取不到这个bean的,因为DispatcherServlet将它放入到了自已的成员变量中
         */
        @Bean
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            return new RequestMappingHandlerMapping();
        }
        
        public class A10Application {
        public static void main(String[] args) throws Exception {
            AnnotationConfigServletWebServerApplicationContext context =
                    new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
            // 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
            RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
            // 获取映射结果
            handlerMapping.getHandlerMethods().forEach((k, v) -> {
                System.out.println(k + "=" + v);
            });
    
            // 请求来了,获取控制器方法,返回控制链结果
            HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/test1"));
            System.out.println(chain);
    
        }
    }
    

    详解SpringMVC_第2张图片

  • RequestMappingHandlerAdapter用途

       @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            return new RequestMappingHandlerAdapter();
        }
        
        public class A10Application {
        public static void main(String[] args) throws Exception {
            AnnotationConfigServletWebServerApplicationContext context =
                    new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
            // 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
            RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
            // 获取映射结果
            handlerMapping.getHandlerMethods().forEach((k, v) -> {
                System.out.println(k + "=" + v);
            });
    
            // RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
            MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
            request.setParameter("name", "张三");
            MockHttpServletResponse response = new MockHttpServletResponse();
    
            HandlerExecutionChain chain = handlerMapping.getHandler(request);
            System.out.println(chain);
            // RequestMappingHandlerAdapter作用:调用控制器
            RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
            adapter.handle(request, response, chain.getHandler());
            System.out.println(">>>>>>>>>>>>>>>>> 参数解析器");
            for (HandlerMethodArgumentResolver resolver : adapter.getArgumentResolvers()) {
                System.out.println(resolver);
            }
            System.out.println(">>>>>>>>>>>>>>>>> 返回值解析器");
            for (HandlerMethodReturnValueHandler returnValueHandler : adapter.getReturnValueHandlers()) {
                System.out.println(returnValueHandler);
            }
        }
    }
    
  • 自定义参数解析器

    
        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
            // 自定义参数解析器
            TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
            adapter.setArgumentResolvers(Arrays.asList(tokenArgumentResolver));
            return adapter;
        }
        
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Token {
    }
    
    
    public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
        // 是否支持某个参数
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            Token annotation = parameter.getParameterAnnotation(Token.class);
    
            return annotation != null;
        }
    
        // 解析参数
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            return webRequest.getHeader("token");
        }
    }
    
    
    public class A10Application {
        public static void main(String[] args) throws Exception {
            AnnotationConfigServletWebServerApplicationContext context =
                    new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
            // 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
            RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
            // 获取映射结果
            handlerMapping.getHandlerMethods().forEach((k, v) -> {
                System.out.println(k + "=" + v);
            });
    
            // RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
            MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
            request.addHeader("token","令牌");
            MockHttpServletResponse response = new MockHttpServletResponse();
    
            HandlerExecutionChain chain = handlerMapping.getHandler(request);
            System.out.println(chain);
            // RequestMappingHandlerAdapter作用:调用控制器
            RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
            adapter.handle(request, response, chain.getHandler());
        }
    }
    
        @PutMapping("/test3")
        public ModelAndView test3(@Token String token){
            System.out.println(token);
            return null;
        }
    
  • 自定义结果处理器

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Yml {
    }
    
    public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {
        @Override
        public 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 {
            // 转化结果为yaml字符串
            String value = new Yaml().dump(returnValue);
            // 将yaml结果写入响应
            HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
            response.setContentType("text/plain;charset=utf-8");
            response.getWriter().println(value);
            // 设置请求已经处理完毕
            mavContainer.setRequestHandled(true);
        }
    }
    
        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
            // 自定义结果处理器
            YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
            adapter.setReturnValueHandlers(Arrays.asList(ymlReturnValueHandler));
            // 自定义参数解析器
            TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
            adapter.setArgumentResolvers(Arrays.asList(tokenArgumentResolver));
            return adapter;
        }
    
    
    public class A10Application {
        public static void main(String[] args) throws Exception {
            AnnotationConfigServletWebServerApplicationContext context =
                    new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
            // 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
            RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
            // 获取映射结果
            handlerMapping.getHandlerMethods().forEach((k, v) -> {
                System.out.println(k + "=" + v);
            });
    
            // RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
            MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4");
            request.addHeader("token","令牌");
            MockHttpServletResponse response = new MockHttpServletResponse();
    
            HandlerExecutionChain chain = handlerMapping.getHandler(request);
            System.out.println(chain);
            // RequestMappingHandlerAdapter作用:调用控制器
            RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
            adapter.handle(request, response, chain.getHandler());
            // 查看响应
            String result = response.getContentAsString(StandardCharsets.UTF_8);
            System.out.println(result);
        }
    }
    

    详解SpringMVC_第3张图片

3.参数解析器

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.RequestParamMethodArgumentResolver@abbc908
        org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@44afefd5
        org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@9a7a808
        org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@72209d93
        org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@2687f956
        org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@1ded7b14
        org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@29be7749
        org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@5f84abe8
        org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@4650a407
        org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@30135202
        org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@6a4d7f76
        org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@10ec523c
        org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@53dfacba
        org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@79767781
        org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@78411116
        org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@aced190
        org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@245a060f
        org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@6edaa77a
        org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@1e63d216
        org.springframework.web.method.annotation.ModelMethodProcessor@62ddd21b
        org.springframework.web.method.annotation.MapMethodProcessor@16c3ca31
        org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@2d195ee4
        org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@2d6aca33
        org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@21ab988f
        org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@29314cc9
        org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@4e38d975
        org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@35f8a9d3
 */
public class A21 {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 准备测试 Request
        HttpServletRequest request = mockRequest();

        // 要点1. 控制器方法被封装为 HandlerMethod
        HandlerMethod 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 表示必须有 @RequestParam
                    new RequestParamMethodArgumentResolver(beanFactory, false),
                    new PathVariableMethodArgumentResolver(),
                    new RequestHeaderMethodArgumentResolver(beanFactory),
                    new ServletCookieValueMethodArgumentResolver(beanFactory),
                    new ExpressionValueMethodArgumentResolver(beanFactory),
                    new ServletRequestMethodArgumentResolver(),
                    new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
                    new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),
                    new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
                    new 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)));
        Map<String, 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,        // age=18
                @RequestParam(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,          // name=zhang&age=18
                User user2,                          // name=zhang&age=18
                @RequestBody User user3              // json
        ) {
        }
    }
                           
    @Data
    @AllArgsConstructor
    @NoArgsConstructor             
    static class User {
        private String name;
        private int age;
    }
}

4.对象绑定与类型转换

1.转换接口与实现

  • 第一套底层转换接口与实现

    详解SpringMVC_第4张图片

    • printer把其它类型转换为string
    • parser把string转换为其它类型
    • formatter综合printer与parser功能
    • convert把类型S转为T
    • printer、parser、convert适配转成GenericConverter放入converters集合
    • formattingConversionService利用它们实现转换
  • 第二套底层转换接口与实现

    • jdk自带的
    • propertyEditor把string与其它类型相互转换
    • propertyEditorRegistry可以注册多个propertyEditor对象
    • 与第一套接口直接可以通过formattingPropertyEditorAdapter来进行适配

详解SpringMVC_第5张图片

  • 高级转换接口与实现

    • 它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
      • 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)
      • 再看有没有 ConversionService 转换
      • 再利用默认的 PropertyEditor 转换
      • 最后有一些特殊处理
    • SimpleTypeConverter 仅做类型转换
    • BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property
    • DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
    • ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能

    详解SpringMVC_第6张图片

public class TestDataBinder {
    public static void main(String[] args) {
        MyBean myBean = new MyBean();
/*      BeanWrapperImpl
        BeanWrapperImpl wrapper = new BeanWrapperImpl(myBean);
        wrapper.setPropertyValue("a",10);
        wrapper.setPropertyValue("b","b");
        wrapper.setPropertyValue("c","2023/01/01");
        System.out.println(myBean);
*/

/*      DirectFieldAccessor
        DirectFieldAccessor accessor = new DirectFieldAccessor(myBean);
        accessor.setPropertyValue("a",10);
        accessor.setPropertyValue("b","b");
        accessor.setPropertyValue("c","2023/01/01");
        System.out.println(myBean);
*/
/*       ServletRequestDataBinder
        ServletRequestDataBinder binder = new ServletRequestDataBinder(myBean);
        MutablePropertyValues values = new MutablePropertyValues();
        values.add("a",10);
        values.add("b","b");
        values.add("c","2023/01/01");
        binder.bind(values);
        System.out.println(myBean);*/

    }

    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;
        }

        @Override
        public String toString() {
            return "User{" +
                    "a=" + a +
                    ", b='" + b + '\'' +
                    ", c=" + c +
                    '}';
        }
    }
}
public class TestServletDataBinder {
    public static void main(String[] args) {

        MyBean myBean = new MyBean();
        ServletRequestDataBinder binder = new ServletRequestDataBinder(myBean);
        // initDirectFieldAccess直接与Filed进行属性绑定 不需要set方法
        binder.initDirectFieldAccess();
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("a","19");
        request.setParameter("b","10");
        request.setParameter("c","2023/01/01");
        binder.bind(new ServletRequestParameterPropertyValues(request));
        System.out.println(myBean);
    }

    static class MyBean {
        private int a;
        private String b;
        private Date c;

        @Override
        public String toString() {
            return "User{" +
                    "a=" + a +
                    ", b='" + b + '\'' +
                    ", c=" + c +
                    '}';
        }
    }
}

5.绑定工厂

  • Date类型格式为yyyy|MM|dd 如果使用以下方式与对象绑定,birthday=null,因为默认的转换器无法识别此格式的类型,所以需要添加自定义转换器来绑定

  • ServletRequestDataBinder binder = new ServletRequestDataBinder(user);
    binder.bind(new ServletRequestParameterPropertyValues(request));
    
public class TestDataBinderFactory {
    public static void main(String[] args) throws Exception {

        User user = new User();

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("birthday","2023|01|01");
        request.setParameter("address.name","湘潭");

        /*
            1.用工厂,无转换功能
                ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,null);
                WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
         */
        /*
            2.用InitBinder转换 底层走是的jdk的PropertyEditorRegistry
                InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(new MyController(),MyController.class.getMethod("aaa", WebDataBinder.class));
                ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Arrays.asList(invocableHandlerMethod),null);
                WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
         */
        /*
            3.用ConversionService转换
                ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
                FormattingConversionService conversionService = new FormattingConversionService();
                conversionService.addFormatter(new MyDateFormatter("用ConversionService方式扩展的"));
                initializer.setConversionService(conversionService);
                ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,initializer);
                WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
         */
        /*
            4.使用默认用ConversionService转换 配合@DateTimeFormat使用
         */
        ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
        ApplicationConversionService conversionService = new ApplicationConversionService();
        initializer.setConversionService(conversionService);
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,initializer);
        WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
        binder.bind(new ServletRequestParameterPropertyValues(request));
        System.out.println(user);

    }

    static class MyController{
        @InitBinder
        public void aaa (WebDataBinder binder){
            binder.addCustomFormatter(new MyDateFormatter("用InitBinder方式扩展的"));
        }
    }

    static class User{
        @DateTimeFormat(pattern = "yyyy|MM|dd")
        private Date birthday;
        private Address address;

        public Date getBirthday() {
            return birthday;
        }

        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }

        public Address getAddress() {
            return address;
        }

        public void setAddress(Address address) {
            this.address = address;
        }

        @Override
        public String toString() {
            return "User{" +
                    "birthday=" + birthday +
                    ", address=" + address +
                    '}';
        }
    }

    static class Address{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Address{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

public class MyDateFormatter implements Formatter<Date> {
    private static final Logger log = LoggerFactory.getLogger(MyDateFormatter.class);
    private final String desc;

    public MyDateFormatter(String desc) {
        this.desc = desc;
    }

    @Override
    public String print(Date date, Locale locale) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.format(date);
    }

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        log.debug(">>>>>> 进入了: {}", desc);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.parse(text);
    }

}

6.获取泛型参数

详解SpringMVC_第7张图片

7.控制器方法执行流程

详解SpringMVC_第8张图片

详解SpringMVC_第9张图片

详解SpringMVC_第10张图片

@Configuration
public class WebConfig {

    @Controller
    static class Controller1 {
        @ResponseStatus(HttpStatus.OK)  // 先不使用ReturnValueHandlerComposite 方便测试
        public ModelAndView foo(User user) {  // user加入到ModelAndViewContainer中 结果= user:User{name='张三'}
            System.out.println("user"+":"+user);
            return null;
        }

    }

    static class User{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

public class A11Application {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(WebConfig.class);


        MockHttpServletRequest request  = new MockHttpServletRequest();
        request.setParameter("name","张三");
        /*
            通过ServletInvocableHandlerMethod把这些整合在一起,并完成控制器方法的调用
        */
        ServletInvocableHandlerMethod method  = new ServletInvocableHandlerMethod(new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo", WebConfig.User.class));
        method.setDataBinderFactory(new ServletRequestDataBinderFactory(null,null));
        method.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
        method.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));

        ModelAndViewContainer container = new ModelAndViewContainer();
        method.invokeAndHandle(new ServletWebRequest(request),container);


        context.close();
    }

    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(Arrays.asList(new MappingJackson2HttpMessageConverter())),
                new ServletModelAttributeMethodProcessor(true),
                new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true)
        );
        return composite;
    }
}

8.返回值处理器

@Configuration
public class WebConfig {

    @Bean
    public 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() {
            @Override
            protected AbstractUrlBasedView instantiateView() {
                FreeMarkerView view = new FreeMarkerView() {
                    @Override
                    protected boolean isContextRequired() {
                        return false;
                    }
                };
                view.setConfiguration(configurer.getConfiguration());
                return view;
            }
        };
        resolver.setContentType("text/html;charset=utf-8");
        resolver.setPrefix("/");
        resolver.setSuffix(".ftl");
        resolver.setExposeSpringMacroHelpers(false);
        return resolver;
    }
}
/*
    目标: 解析控制器方法的返回值
    常见的返回值处理器
        org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@4c9e38
        org.springframework.web.method.annotation.ModelMethodProcessor@5d1e09bc
        org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@4bdc8b5d
        org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@3bcd426c
        org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@5f14a673
        org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@726a17c4
        org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@5dc3fcb7
        org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@c4c0b41
        org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@76911385
        org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@5467eea4
        org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@160396db
        org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@7a799159
        org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@40ab8a8
        org.springframework.web.method.annotation.MapMethodProcessor@6ff37443
        org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@65cc8228
 */
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 HttpEntity<User> 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;
        }

        @ResponseBody
        public 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;
        }

        @Override
        public String toString() {
            return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
        }
    }
}

9.MessageConverter

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));
    }

    @ResponseBody
    @RequestMapping(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;

        @JsonCreator
        public 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;
        }

        @Override
        public String toString() {
            return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
        }
    }
}

10.ControllerAdvice

1.配合@InitBinder

  • 作用:所有控制器需要自定义类型转换器时,可以用@InitBinder补充自定义的转换器

2.配合@ModelAttribute

  • 作用:方法的返回值会作为模型数据补充到控制器的执行过程中

3.Response/RequestBodyAdvice

  • 作用:对请求体或者响应体增强或者扩展,本质上对消息的一个扩展(读消息写消息时做扩展)
@Configuration
public class WebConfig {

    @ControllerAdvice
    static class MyControllerAdvice implements ResponseBodyAdvice<Object> {
        // 满足条件才转换
        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
    // @ResponseBody
    @RestController
    public 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;
        }
    }
}

11.异常处理

public class ExceptionHandlerDemo {
    public static void main(String[] args) throws NoSuchMethodException, UnsupportedEncodingException {
        ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();

        resolver.setMessageConverters(ListUtil.of(new MappingJackson2HttpMessageConverter()));
        // 默认参数解析器、结果解析器
        resolver.afterPropertiesSet();
        /*
               测试json
                    MockHttpServletRequest request = new MockHttpServletRequest();
                    MockHttpServletResponse response = new MockHttpServletResponse();
                    HandlerMethod handler = new HandlerMethod(new Controller1(),Controller1.class.getMethod("foo"));
                    resolver.resolveException(request,response,handler,new ArithmeticException("被零整除了"));
                    System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
         */
        /*
               测试mav
                    MockHttpServletRequest request = new MockHttpServletRequest();
                    MockHttpServletResponse response = new MockHttpServletResponse();
                    HandlerMethod handler = new HandlerMethod(new Controller2(),Controller2.class.getMethod("foo"));
                    ModelAndView modelAndView = resolver.resolveException(request, response, handler, new ArithmeticException("被零整除"));
                    System.out.println(modelAndView.getViewName());
                    System.out.println(modelAndView.getModelMap());
         */
        /*
               嵌套异常
                    MockHttpServletRequest request = new MockHttpServletRequest();
                    MockHttpServletResponse response = new MockHttpServletResponse();
                    HandlerMethod handler = new HandlerMethod(new Controller3(),Controller3.class.getMethod("foo"));
                    RuntimeException e = new RuntimeException("e2", new IOException("e3"));
                    resolver.resolveException(request,response,handler,e);
                    System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
         */
        // 异常处理方法参数解析
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        HandlerMethod handler = new HandlerMethod(new Controller4(), Controller4.class.getMethod("foo"));
        Exception e = new Exception("a");
        resolver.resolveException(request, response, handler, e);
        System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));

    }

    static class Controller1 {
        public void foo() {
        }

        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handler(ArithmeticException e) {
            return MapUtil.of("error", e.getMessage());
        }
    }

    static class Controller2 {
        public void foo() {
        }

        @ExceptionHandler
        public ModelAndView handler(ArithmeticException e) {
            return new ModelAndView("test2", MapUtil.of("error", e.getMessage()));
        }
    }

    static class Controller3 {
        public void foo() {
        }

        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handler(IOException e3) {
            return MapUtil.of("error", e3.getMessage());
        }
    }

    static class Controller4 {
        public void foo() {
        }

        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handler(Exception e, HttpServletRequest request) {
            System.out.println(request);
            return MapUtil.of("error", e.getMessage());
        }
    }
}
// 全局通用异常
@Configuration
public class WebConfig {
    @ControllerAdvice
    static class MyControllerAdvice {
        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handle(Exception e) {
            return Map.of("error", e.getMessage());
        }
    }

    @Bean
    public ExceptionHandlerExceptionResolver resolver() {
        ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
        resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
        return resolver;
    }
}

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() {

        }
    }
}

12.HandlerMapping与HandlerAdapter

  • SimpleControllerHandlerAdapter与BeanNameUrlHandlerMapping
    • 通过beanName(一定要带/)找到控制器,然后SimpleControllerHandlerAdapter调用控制器执行
@Configuration
public class WebConfig {

    // 内嵌web容器工厂
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    // 创建DispatcherServlet
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    // 注册DispatcherServlet到tomcat
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        return registrationBean;
    }

    @Bean
    public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter(){
        return new SimpleControllerHandlerAdapter();
    }

    @Bean
    public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping(){
        return new BeanNameUrlHandlerMapping();
    }

    @Component("/test1")
    public static class Controller1 implements Controller {

        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getWriter().println("this is Controller1");
            return null;
        }
    }

    @Component("/test2")
    public static class Controller2 implements Controller {

        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getWriter().println("this is Controller2");
            return null;
        }
    }


    @Bean("/test3")
    public Controller Controller3() {
        return (request, response) -> {
            response.getWriter().println("this is Controller3");
            return null;
        };
    }
}

public class A4Application {
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
    }
}
  • 自定义HandlerMapping与HandlerAdapter

    package com.liubo.springmvc.a4;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.*;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.annotation.PostConstruct;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    /**
     * @author lb
     * @date 2023/1/2 1:36 下午
     */
    @Configuration
    public class WebConfig {
    
        // 内嵌web容器工厂
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    
        // 创建DispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
    
        // 注册DispatcherServlet到tomcat
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
            return registrationBean;
        }
    
        // SimpleUrlHandlerMapping
        @Component
        static class MyHandlerMapping implements HandlerMapping {
    
            @Override
            public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                String uri = request.getRequestURI();
                Controller controller = controllerMap.get(uri);
                if (controller == null){
                    return null;
                }
    
                return new HandlerExecutionChain(controller);
            }
    
            @Autowired
            private ApplicationContext applicationContext;
    
            private Map<String, Controller> controllerMap;
    
            @PostConstruct
            public void init(){
                controllerMap = applicationContext.getBeansOfType(Controller.class)
                        .entrySet().stream().filter(e -> e.getKey().startsWith("/")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            }
        }
    
        // SimpleControllerHandlerAdapter
        @Component
        static class MyHandlerAdapter implements HandlerAdapter{
            @Override
            public boolean supports(Object handler) {
                return handler instanceof Controller;
            }
    
            @Override
            public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                if (handler instanceof Controller){
                    ((Controller) handler).handleRequest(request, response);
                }
                return null;
            }
    
            @Override
            public long getLastModified(HttpServletRequest request, Object handler) {
                return 0;
            }
        }
    
    
    
        @Component("/test1")
        public static class Controller1 implements Controller {
    
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().println("this is Controller1");
                return null;
            }
        }
    
        @Component("/test2")
        public static class Controller2 implements Controller {
    
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().println("this is Controller2");
                return null;
            }
        }
    
    
        @Bean("/test3")
        public Controller Controller3() {
            return (request, response) -> {
                response.getWriter().println("this is Controller3");
                return null;
            };
        }
    }
    
    public class A4Application {
        public static void main(String[] args) {
            AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
        }
    }
    
  • RouterFunctionMapping与HandlerFunctionAdapter

    • RouterFunctionMapping,收集所有RouterFunction,它包括两部分:Requestpredicate设置映射条件,HandlerFunction包含处理逻辑,请求到达根据映射条件找到HandlerFunction,即handler,HandlerFunctionAdapter调用handler
    @Configuration
    public class WebConfig1 {
        @Bean // ⬅️内嵌 web 容器工厂
        public TomcatServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory(8080);
        }
    
        @Bean // ⬅️创建 DispatcherServlet
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
    
        @Bean // ⬅️注册 DispatcherServlet, Spring MVC 的入口
        public DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
    
        @Bean
        public RouterFunctionMapping routerFunctionMapping() {
            return new RouterFunctionMapping();
        }
    
        @Bean
        public HandlerFunctionAdapter handlerFunctionAdapter() {
            return new HandlerFunctionAdapter();
        }
    
        @Bean
        public RouterFunction<ServerResponse> r1() {
            return route(GET("/r1"), request -> ok().body("this is r1"));
        }
    
        @Bean
        public RouterFunction<ServerResponse> r2() {
            return route(GET("/r2"), request -> ok().body("this is r2"));
        }
    }
    
    
    public class A4Application {
        public static void main(String[] args) {
            AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
        }
    }
    

    详解SpringMVC_第11张图片

13.mvc执行流程

当浏览器发送一个请求 http://localhost:8080/hello 后,请求到达服务器,其处理流程是:

  1. 服务器提供了 DispatcherServlet,它使用的是标准 Servlet 技术

    • 路径:默认映射路径为 /,即会匹配到所有请求 URL,可作为请求的统一入口,也被称之为前控制器
      • jsp 不会匹配到 DispatcherServlet
      • 其它有路径的 Servlet 匹配优先级也高于 DispatcherServlet
    • 创建:在 Boot 中,由 DispatcherServletAutoConfiguration 这个自动配置类提供 DispatcherServlet 的 bean
    • 初始化:DispatcherServlet 初始化时会优先到容器里寻找各种组件,作为它的成员变量
      • HandlerMapping,初始化时记录映射关系
      • HandlerAdapter,初始化时准备参数解析器、返回值处理器、消息转换器
      • HandlerExceptionResolver,初始化时准备参数解析器、返回值处理器、消息转换器
      • ViewResolver
  2. DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法

    • 例如根据 /hello 路径找到 @RequestMapping(“/hello”) 对应的控制器方法

    • 控制器方法会被封装为 HandlerMethod 对象,并结合匹配到的拦截器一起返回给 DispatcherServlet

    • HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain(调用链)对象

  3. DispatcherServlet 接下来会:

    1. 调用拦截器的 preHandle 方法
    2. 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 步走视图解析及渲染流程
    3. 调用拦截器的 postHandle 方法
    4. 处理异常或视图渲染
      • 如果 1~3 出现异常,走 ExceptionHandlerExceptionResolver 处理异常流程
        • @ControllerAdvice 全局增强点5️⃣:@ExceptionHandler 异常处理
      • 正常,走视图解析及渲染流程
    5. 调用拦截器的 afterCompletion 方法

你可能感兴趣的:(spring,java,servlet)