【第二十讲】

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

    }
}

配置类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,"/");
    }


}

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

【1】disPatcherServlet 初始化

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

访问地址:localhost:8080

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

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

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

spring.application.name=spring-01
server.port=8081

logging.level.root = info

spring.mvc.servlet.load-on-startup=1

在这里插入图片描述

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

【3】RequestMappingHandlerMapping基本用途

注册一个RequestMappingHandlerMapping

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(){
    return new RequestMappingHandlerMapping();
}

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

注册一个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);
    }
}

利用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());

    }

在这里插入图片描述

RequestMappingHandlerAdapter 参数和返回值解析器

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

自定义参考数解析器

/test3中

@PutMapping("/test3")
public ModelAndView test3(@Token String token) {
log.debug("test3({})", token);
return null;
}

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

配置类

@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
    TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
    handlerAdapter.setCustomArgumentResolvers(Arrays.asList(tokenArgumentResolver));
    return handlerAdapter;
}

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

在这里插入图片描述

自定义返回值处理器

@RequestMapping("/test4")
//    @ResponseBody
@Yml
public User test4() {
    log.debug("test4");
    return new User("张三", 18);
}

自定义参数解析器

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

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

返回结果

在这里插入图片描述

总结

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 处理控制器方法返回值
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐