初识 DispatcherServlet

149 阅读7分钟
  • DispatcherServlet 的初始化时机
  • DispatcherServlet 初始化都做了什么
  • RequestMappingHandlerMapping 基本用途
  • RequestMappingHandlerAdapter 基本用途
  • RequestMappingHandlerAdapter 自定义参数和返回值处理器

参考代码:/spring-demo/springmvc-study/src/main/java/com/exam/springmvc/dispatcherservlet/DispatcherservletDemo.java

DispatcherServlet

DispatcherServlet 的初始化时机

要观察 DispatcherServlet 的初始化时机过程,我们自己内嵌 WEB 容器配置类支持内嵌 WEB 容器的 ApplicationContext(AnnotationConfigServletWebServerApplicationContext)

要支持内嵌的 WEB 容器的 Spring 容器,有三项是必须配置的

  1. 内嵌 web 容器工厂
  2. 创建 DispatcherServlet
  3. 注册 DispatcherServlet, Spring MVC 的入口
/**
 * 内嵌 web 容器配置信息
 */
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties") // 使用配置文件中的参数
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) // spring 中获取配置文件中前缀对象注解
public class MyWebConfig {

    // 1. 内嵌 web 容器工厂
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {
        // 获取配置文件中 server.port 的值
        return new TomcatServletWebServerFactory(serverProperties.getPort());
    }

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

    // 3. 注册 DispatcherServlet, Spring MVC 的入口
    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        return registrationBean;
    }
}
  1. 启动类
public class DispatcherservletDemo {

    private static final Logger log = LoggerFactory.getLogger(DispatcherservletDemo.class);

    public static void main(String[] args) {
        // 支持内嵌 web 容器的 spring 容器
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(MyWebConfig.class);
    }
}
  1. 启动日志分析
  1. 发送第一次 request,dispatcherServlet 对象才开始初始化
  1. 调整 dispatcherServlet 对象初始化时机
  • 默认 dispatcherServlet 对象初始化时机是第一次request时,先调整为当 tomcat 容器启动时,初始化 dispatcherServlet 对象;
  • 原因:org.springframework.boot.web.servlet.ServletRegistrationBean 中 loadOnStartup 的默认值为 -1;
  • 解决:loadOnStartup 值大于 -1 即可;
  • 修改 MyWebConfig 中 DispatcherServletRegistrationBean 的 setLoadOnStartup 值;
/**
 * 内嵌 web 容器配置信息
 */
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties") // 使用配置文件中的参数
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) // spring 中获取配置文件中前缀对象注解
public class MyWebConfig {

    ......

    // 3. 注册 DispatcherServlet, Spring MVC 的入口
    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        // 获取配置文件中 spring.mvc.servlet.load-on-startup 的值(spring.mvc.servlet.load-on-startup=1)
        registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
        return registrationBean;
    }
}

DispatcherServlet 初始化都做了什么

参考源码;org.springframework.web.servlet.DispatcherServlet#onRefresh

@Override
protected void onRefresh(ApplicationContext context) {
    // 初始化各个组件
    initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
    // 初始化文件解析器
    initMultipartResolver(context);
    // 初始化本地化信息,如:那个地区,那个国家
    initLocaleResolver(context);
    // 用于准备DispatcherServlet处理请求时所使用的ThemeResolver策略组件对象
    initThemeResolver(context);
    // 初始化路径映射
    initHandlerMappings(context);
    // 适配不同形式的控制器方法
    initHandlerAdapters(context);
    // 解析处理器中的异常
    initHandlerExceptionResolvers(context);
    // 主要用于理请求时所使用的策略
    initRequestToViewNameTranslator(context);
    // 初始视图处理器
    initViewResolvers(context);
    // 用于生成FlashMap管理器
    initFlashMapManager(context);
}

随便挑一个(initHandlerMappings),简单的看一下实现

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    // detectAllHandlerMappings 用来判断是否检测所有的容器
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    // 只检测当前容器
    else {
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }

    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
	// 容器中没有 handlerMappings,则获取默认的
    if (this.handlerMappings == null) {
        // 默认的 handlerMappings,见下面源码
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }

    for (HandlerMapping mapping : this.handlerMappings) {
        if (mapping.usesPathPatterns()) {
            this.parseRequestPath = true;
            break;
        }
    }
}


// 获取默认配置信息
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    if (defaultStrategies == null) {
        try {
            // Load default strategy implementations from properties file.
            // This is currently strictly internal and not meant to be customized
            // by application developers.
            // DEFAULT_STRATEGIES_PATH 默认配置文件中信息
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
        }
    }

    String key = strategyInterface.getName();
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        List<T> strategies = new ArrayList<>(classNames.length);
        for (String className : classNames) {
            try {
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
            }
            catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                        "Could not find DispatcherServlet's default strategy class [" + className +
                        "] for interface [" + key + "]", ex);
            }
            catch (LinkageError err) {
                throw new BeanInitializationException(
                        "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                        className + "] for interface [" + key + "]", err);
            }
        }
        return strategies;
    }
    else {
        return Collections.emptyList();
    }
}

RequestMappingHandlerMapping 的基本用途

RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 以及派生注解(如:@GetMapping) 映射信息,封装为 Map,其中

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

    private static final Logger log = LoggerFactory.getLogger(MyController.class);

    @GetMapping("/test1")
    public ModelAndView test1() throws Exception {
        log.debug("test1()");
        return null;
    }

    @PostMapping("/test2")
    public ModelAndView test2(@RequestParam("name") String name) {
        log.debug("test2({})", name);
        return null;
    }

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

    @RequestMapping("/test4")
    // @ResponseBody
    @MyYml
    public User test4() {
        log.debug("test4");
        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 int getAge() {
            return age;
        }

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

        public void setAge(int age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        String str = new Yaml().dump(new User("张三", 18));
        System.out.println(str);
    }
}
  • main 方法
public class DispatcherservletDemo {

    private static final Logger log = LoggerFactory.getLogger(DispatcherservletDemo.class);

    public static void main(String[] args) throws Exception {
        // 支持内嵌 web 容器的 spring 容器
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(MyWebConfig.class);
        // 获取 RequestMappingHandlerMapping bean 对象
        // 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodsMap = handlerMapping.getHandlerMethods();
        log.debug("RequestMappingHandlerMapping 中生成路径与控制器方法的映射关系 ===>");
        handlerMethodsMap.forEach((k, v) -> log.debug("K = {} , V = {}", k, v));

        // 模拟请求,访问 test1
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("GET", "/test1");

        // 返回处理器链对象
        HandlerExecutionChain chain = handlerMapping.getHandler(mockHttpServletRequest);
        log.debug("处理器链对象:{}", chain);
    }
}
  • MyWebConfig

    如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 spring 容器中的 bean,导致获取不到,需要在 MyWebConfig 中将 RequestMappingHandlerMapping 加入到 spring 容器中

// 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 spring 容器中的 bean,导致获取不到
// 1. 将 RequestMappingHandlerMapping 加入到 spring 容器中
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    return new RequestMappingHandlerMapping();
}
  • 运行日志:其中 interceptors 为拦截器

RequestMappingHandlerAdapter 基本用途

RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:

  1. HandlerMethodArgumentResolver 解析控制器方法参数
  2. HandlerMethodReturnValueHandler 处理控制器方法返回值
  • MyWebConfig
    • 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 spring 容器中的 bean,导致获取不到,需要在 MyWebConfig 中将 RequestMappingHandlerAdapter 加入到 spring 容器中
    • 因为 RequestMappingHandlerAdapter 中的 invokeHandlerMethod 是 protected,测试类无法直接调用,通过 extend 修改方法的作用域
// 2. 继续加入 RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapter
// 因为 RequestMappingHandlerAdapter 中的 invokeHandlerMethod 是 protected,测试类无法直接调用,通过 extend 修改方法的作用域
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
    return handlerAdapter;
}
  • RequestMappingHandlerAdapter 获取参数处理器
public class DispatcherservletDemo {

    private static final Logger log = LoggerFactory.getLogger(DispatcherservletDemo.class);

    public static void main(String[] args) throws Exception {
        // 支持内嵌 web 容器的 spring 容器
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(MyWebConfig.class);
        // 获取 RequestMappingHandlerMapping bean 对象
        // 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodsMap = handlerMapping.getHandlerMethods();
        log.debug("RequestMappingHandlerMapping 中生成路径与控制器方法的映射关系 ===>");
        handlerMethodsMap.forEach((k, v) -> log.debug("K = {} , V = {}", k, v));

        // 模拟请求,访问 test1
        // MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("GET", "/test1");

        // 模拟请求,访问 test2
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("POST", "/test2");
        mockHttpServletRequest.addParameter("name", "王小闹");

        MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();

        // 返回处理器链对象
        HandlerExecutionChain chain = handlerMapping.getHandler(mockHttpServletRequest);
        log.debug("处理器链对象:{}", chain);

        log.debug("< ---------- 调用控制器方法 ---------- >");
        // HandlerAdapter 作用: 调用控制器方法
        MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
        handlerAdapter.invokeHandlerMethod(mockHttpServletRequest, mockHttpServletResponse, (HandlerMethod)chain.getHandler());

        // handlerAdapter 中参数解析器
        log.debug("< ---------- handlerAdapter 中参数解析器 ---------- >");
        for (HandlerMethodArgumentResolver argumentResolver : Objects.requireNonNull(handlerAdapter.getArgumentResolvers())) {
            log.debug("{}", argumentResolver);
        }
        // handlerAdapter 中响应参数解析器
        log.debug("< ---------- handlerAdapter 中参数解析器 ---------- >");
        for (HandlerMethodReturnValueHandler returnValueHandler : Objects.requireNonNull(handlerAdapter.getReturnValueHandlers())) {
            log.debug("{}", returnValueHandler);
        }
    }
}
  • 运行日志
日志:DispatcherservletDemo - RequestMappingHandlerMapping 中生成路径与控制器方法的映射关系 ===>
日志:DispatcherservletDemo - K = {POST [/test2]} , V = com.exam.springmvc.dispatcherservlet.MyController#test2(String)
日志:DispatcherservletDemo - K = {GET [/test1]} , V = com.exam.springmvc.dispatcherservlet.MyController#test1()
日志:DispatcherservletDemo - K = { [/test4]} , V = com.exam.springmvc.dispatcherservlet.MyController#test4()
日志:DispatcherservletDemo - K = {PUT [/test3]} , V = com.exam.springmvc.dispatcherservlet.MyController#test3(String)
21:22:55.775 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.exam.springmvc.dispatcherservlet.MyController#test2(String)
日志:DispatcherservletDemo - 处理器链对象:HandlerExecutionChain with [com.exam.springmvc.dispatcherservlet.MyController#test2(String)] and 0 interceptors
日志:DispatcherservletDemo - < ---------- 调用控制器方法 ---------- >
21:22:55.783 [main] DEBUG com.exam.springmvc.dispatcherservlet.MyController - test2(王小闹)
日志:DispatcherservletDemo - < ---------- handlerAdapter 中参数解析器 ---------- >
日志:DispatcherservletDemo - org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@3af17be2
日志:DispatcherservletDemo - org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@f9879ac
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@37f21974
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@5f4d427e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@6e521c1e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@224b4d61
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@5d5d9e5
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@303e3593
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@4ef27d66
日志:DispatcherservletDemo - org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@362a019c
日志:DispatcherservletDemo - org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@1d9bec4d
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@5c48c0c0
日志:DispatcherservletDemo - org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@10c8f62
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@674c583e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@25f7391e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@3f23a3a0
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@5ab14cb9
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@5fb97279
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@439a8f59
日志:DispatcherservletDemo - org.springframework.web.method.annotation.ModelMethodProcessor@61861a29
日志:DispatcherservletDemo - org.springframework.web.method.annotation.MapMethodProcessor@31024624
日志:DispatcherservletDemo - org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@25bcd0c7
日志:DispatcherservletDemo - org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@32cb636e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@63cd604c
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@40dd3977
日志:DispatcherservletDemo - org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@3a4e343
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@6a1d204a
日志:DispatcherservletDemo - < ---------- handlerAdapter 中参数解析器 ---------- >
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@62dae245
日志:DispatcherservletDemo - org.springframework.web.method.annotation.ModelMethodProcessor@4b6579e8
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@6fff253c
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@6c6357f9
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@591e58fa
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@3954d008
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@2f94c4db
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@593e824f
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@72ccd81a
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@6d8792db
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@64bc21ac
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@493dfb8e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@5d25e6bb
日志:DispatcherservletDemo - org.springframework.web.method.annotation.MapMethodProcessor@ce5a68e
日志:DispatcherservletDemo - org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@9d157ff

自定义参数与返回值处理器

  • MyWebConfig
// 2. 继续加入 RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapter
// 因为 RequestMappingHandlerAdapter 中的 invokeHandlerMethod 是 protected,测试类无法直接调用,通过 extend 修改方法的作用域
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    MyRequestMappingHandlerAdapter myHandlerAdapter = new MyRequestMappingHandlerAdapter();
    // 参数解析器
    List<HandlerMethodArgumentResolver> argumentResolverList = new ArrayList<>();
    argumentResolverList.add(new MyTokenArgumentResolver());
    myHandlerAdapter.setArgumentResolvers(argumentResolverList);
    // 响应参数解析器
    List<HandlerMethodReturnValueHandler> returnValueHandlerList = new ArrayList<>();
    returnValueHandlerList.add(new MyYmlReturnValueHandler());
    myHandlerAdapter.setCustomReturnValueHandlers(returnValueHandlerList);
    return myHandlerAdapter;
}
  • 自定义 参数解析器:MyTokenArgumentResolver
/**
 * 自定义 RequestMappingHandlerAdapter 参数解析器
 */
public class MyTokenArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * 是否支持某个参数,返回 true 时,resolveArgument 才执行
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        MyToken myToken = parameter.getParameterAnnotation(MyToken.class);
        return myToken != null;
    }

    /**
     * 解析参数
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
        return webRequest.getHeader("token");
    }
}
  • 自定义响应参数解析器:MyYmlReturnValueHandler
/**
 * 自定义响应参数解析器
 */
public class MyYmlReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        MyYml myYml = returnType.getMethodAnnotation(MyYml.class);
        return myYml != 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);
        assert response != null;
        response.setContentType("text/plain;charset=utf-8");
        response.getWriter().print(str);

        // 3. 设置请求已经处理完毕
        mavContainer.setRequestHandled(true);
    }
}
  • main
public class DispatcherservletDemo {

    private static final Logger log = LoggerFactory.getLogger(DispatcherservletDemo.class);

    public static void main(String[] args) throws Exception {
        // 支持内嵌 web 容器的 spring 容器
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(MyWebConfig.class);
        // 获取 RequestMappingHandlerMapping bean 对象
        // 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodsMap = handlerMapping.getHandlerMethods();
        log.debug("RequestMappingHandlerMapping 中生成路径与控制器方法的映射关系 ===>");
        handlerMethodsMap.forEach((k, v) -> log.debug("K = {} , V = {}", k, v));

        // 模拟请求,访问 test1
        // MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("GET", "/test1");

        // 模拟请求,访问 test2
        // MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("POST", "/test2");
        // mockHttpServletRequest.addParameter("name", "王小闹");

        // 模拟请求,访问 test3
        // MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("PUT", "/test3");
        // mockHttpServletRequest.addHeader("token", "424bhk2424232334");

        // 模拟请求,访问 test3
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("PUT", "/test4");

        MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();

        // 返回处理器链对象
        HandlerExecutionChain chain = handlerMapping.getHandler(mockHttpServletRequest);
        log.debug("处理器链对象:{}", chain);

        log.debug("< ---------- 调用控制器方法 ---------- >");
        // HandlerAdapter 作用: 调用控制器方法
        MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
        handlerAdapter.invokeHandlerMethod(mockHttpServletRequest, mockHttpServletResponse, (HandlerMethod)chain.getHandler());

        // 检查响应
        byte[] content = mockHttpServletResponse.getContentAsByteArray();
        log.debug("自定义响应参数解析器:{}", new String(content, StandardCharsets.UTF_8));

        /*// handlerAdapter 中参数解析器
        log.debug("< ---------- handlerAdapter 中参数解析器 ---------- >");
        for (HandlerMethodArgumentResolver argumentResolver : Objects.requireNonNull(handlerAdapter.getArgumentResolvers())) {
            log.debug("{}", argumentResolver);
        }
        // handlerAdapter 中响应参数解析器
        log.debug("< ---------- handlerAdapter 中参数解析器 ---------- >");
        for (HandlerMethodReturnValueHandler returnValueHandler : Objects.requireNonNull(handlerAdapter.getReturnValueHandlers())) {
            log.debug("{}", returnValueHandler);
        }*/
    }
}