• 【第二十讲】RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter


    【第二十讲】

    RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter

    1. DispatcherServlet 初始化时机
    2. DispatcherServlet 初始化都做了什么
    3. RequestMappingHandlerMapping 基本用途
    4. RequestMappingHandlerAdapter 基本用途
    5. 自定义参数和返回值处理器

    启动容器

    AnnotationConfigServletWebServerApplicationContext不要搞错为

    AnnotationConfigServletWebApplicationContext 否则没法启动

    @Slf4j
    public class A20 {
        public static void main(String[] args) {
    
            AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    配置类WebConfig

    @ComponentScan
    @Configuration
    public class WebConfig {
        //1.内嵌web 容器工厂
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        //2.创建DispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet(){
            return new DispatcherServlet();
        }
    
        //3.注册DispatcherServlet,Spring MVC 的入口
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet){
            return  new DispatcherServletRegistrationBean(dispatcherServlet,"/");
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注册后,成功启动tomact
    在这里插入图片描述

    【1】disPatcherServlet 初始化

    DispatcherServlet 是在第一次访问的时候初始化

    访问地址:localhost:8080
    
    • 1

    通过设置registrationBean.setLoadOnStartup(1);后,在启动tomcat容器后就初始化

    //3.注册DispatcherServlet,Spring MVC 的入口
    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet){
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        registrationBean.setLoadOnStartup(1);
        return  registrationBean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    获取配置文件中 load-on-startup

    spring.application.name=spring-01
    server.port=8081
    
    logging.level.root = info
    
    spring.mvc.servlet.load-on-startup=1
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    【2】DispathcherServlet 初始化都做了什么

    DispathcherServlet.java

    @Override
    	protected void onRefresh(ApplicationContext context) {
    		initStrategies(context);
    	}
    
    
    	protected void initStrategies(ApplicationContext context) {
            // 文件上传解析器
    		initMultipartResolver(context);
            initLocaleResolver(context);
    		initThemeResolver(context);
            // 路径映射
    		initHandlerMappings(context);
    		initHandlerAdapters(context);
    		initHandlerExceptionResolvers(context);
    		initRequestToViewNameTranslator(context);
    		initViewResolvers(context);
    		initFlashMapManager(context);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【3】RequestMappingHandlerMapping基本用途

    注册一个RequestMappingHandlerMapping

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping(){
        return new RequestMappingHandlerMapping();
    }
    
    • 1
    • 2
    • 3
    • 4

    RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中

    • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
    • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
    • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet

    在这里插入图片描述

    【4】RequestMappingHandlerAdapter基本用途

    • 作用:调用控制器方法

    Controller1

    @GetMapping("/test1")
    public ModelAndView test1() throws Exception {
        log.debug("test1()");
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注册一个MyRequestMappingHandlerAdapter该类继承

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
        return new MyRequestMappingHandlerAdapter();
    }
    
    
    public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
        @Override
        public ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
            return super.invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    利用RequestMappingHandlerAdapter 中的invokeHandlerMethod 方法可以调用HandlerMethod

     public static void main(String[] args) throws Exception {
    
            AnnotationConfigServletWebServerApplicationContext context =
                    new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    
            // 作用 解析@RequestMapping 以及派生注解,生成路径与控制器方法的映射关系,在初始化时生成
            RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
            // 获取映射结果
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
            handlerMethods.forEach((k,v) ->{
                System.out.println(k + "==" + v);
            });
    
            // 请求来了,获取控制器方法_返回处理器执行链
            MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test1");
            MockHttpServletResponse response = new MockHttpServletResponse();
            HandlerExecutionChain chain = handlerMapping.getHandler(request);
            System.out.println("chain:---"+chain);
    
            System.out.println("-----------------------------------------");
            MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
            handlerAdapter.invokeHandlerMethod(request,response,(HandlerMethod)chain.getHandler());
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    RequestMappingHandlerAdapter 参数和返回值解析器

    • 参考解析器:handlerAdapter.getArgumentResolvers()
    • 返回值解析器:handlerAdapter.getReturnValueHandlers()
      在这里插入图片描述

    自定义参考数解析器

    /test3中

    @PutMapping("/test3")
    public ModelAndView test3(@Token String token) {
    log.debug("test3({})", token);
    return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    TokenArgumentResolver 加载自定义注解

    并添加到配置类

    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 methodParameter,
                                      ModelAndViewContainer modelAndViewContainer,
                                      NativeWebRequest webRequest,
                                      WebDataBinderFactory webDataBinderFactory) throws Exception {
            return webRequest.getHeader("token");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    配置类

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
        TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
        MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
        handlerAdapter.setCustomArgumentResolvers(Arrays.asList(tokenArgumentResolver));
        return handlerAdapter;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    main

    public static void main(String[] args) throws Exception {
    
        AnnotationConfigServletWebServerApplicationContext context =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    
        // 作用 解析@RequestMapping 以及派生注解,生成路径与控制器方法的映射关系,在初始化时生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    
        // 获取映射结果
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        handlerMethods.forEach((k,v) ->{
            System.out.println(k + "==" + v);
        });
    
        // 请求来了,获取控制器方法_返回处理器执行链
        MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
        request.addHeader("token","某个令牌");
        MockHttpServletResponse response = new MockHttpServletResponse();
        HandlerExecutionChain chain = handlerMapping.getHandler(request);
        System.out.println("chain:---"+chain);
    
        System.out.println("-----------------------------------------");
        // 作用:调用控制器方法
        MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
        handlerAdapter.invokeHandlerMethod(request,response,(HandlerMethod)chain.getHandler());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    自定义返回值处理器

    @RequestMapping("/test4")
    //    @ResponseBody
    @Yml
    public User test4() {
        log.debug("test4");
        return new User("张三", 18);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    自定义参数解析器

    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 {
            // 1. 转换返回结果为 yaml 字符串
            String str = new Yaml().dump(returnValue);
    
            // 2. 将 yaml 字符串写入响应
            HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
            response.setContentType("text/plain;charset=utf-8");
            response.getWriter().print(str);
    
            // 3. 设置请求已经处理完毕
            mavContainer.setRequestHandled(true);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    讲YmlReturnValueHandler 注入到容器中

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
    TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
    YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
    handlerAdapter.setCustomArgumentResolvers(Arrays.asList(tokenArgumentResolver));
    handlerAdapter.setCustomReturnValueHandlers(Arrays.asList(ymlReturnValueHandler));
    return handlerAdapter;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    返回结果

    在这里插入图片描述

    总结

    RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 俩是一对,分别用来

    • 处理 @RequestMapping 映射
    • 调用控制器方法、并处理方法参数与方法返回值
    1. DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化

    2. 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等,并逐一调用它们的初始化

    3. RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中

      • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
      • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
      • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
    4. RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:

      • HandlerMethodArgumentResolver 解析控制器方法参数
      • HandlerMethodReturnValueHandler 处理控制器方法返回值
  • 相关阅读:
    OSG笔记:对线求交失败
    Echarts 中国地图
    redis的原理和源码-源码阅读指南
    计划截止至此(其他照常完善)
    手撕排序之堆排序
    基于PyTorch机器学习与深度学习实践应用与案例分析
    【异步任务】异步线程后台执行解压缩,finished后通知调用者
    十二、Mysql的索引
    抖音招聘直播报白有成本低和招聘效果精准的优势
    使用 DM binary 部署 DM 集群
  • 原文地址:https://blog.csdn.net/qq_25614773/article/details/126513350