请求映射处理

RequestMappingHandlerMapping

在配置类
@Configuration @ComponentScan @PropertySource("classpath:application.properties") @EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) public class WebConfig { // ⬅️内嵌 web 容器工厂 @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) { return new TomcatServletWebServerFactory(serverProperties.getPort()); } // ⬅️创建 DispatcherServlet @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } // ⬅️注册 DispatcherServlet, Spring MVC 的入口 @Bean public DispatcherServletRegistrationBean dispatcherServletRegistrationBean( DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) { DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/"); registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); return registrationBean; } // 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 bean, 给测试带来困扰 // ⬅️1. 加入RequestMappingHandlerMapping @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { return new RequestMappingHandlerMapping(); } }
RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中
  • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
  • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
  • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
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); });
notion image
 
// 请求来了,获取控制器方法 返回处理器执行链对象包括拦截器 MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4"); request.setParameter("name", "张三"); request.addHeader("token", "某个令牌"); MockHttpServletResponse response = new MockHttpServletResponse(); HandlerExecutionChain chain = handlerMapping.getHandler(request); System.out.println(chain);
notion image

RequestMappingHandlerAdapter

WebConfig 中配置RequestMappingHandlerAdapter
@Configuration @ComponentScan @PropertySource("classpath:application.properties") @EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) public class WebConfig { // ⬅️内嵌 web 容器工厂 @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) { return new TomcatServletWebServerFactory(serverProperties.getPort()); } // ⬅️创建 DispatcherServlet @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } // ⬅️注册 DispatcherServlet, Spring MVC 的入口 @Bean public DispatcherServletRegistrationBean dispatcherServletRegistrationBean( DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) { DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/"); registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); return registrationBean; } // 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 bean, 给测试带来困扰 // ⬅️1. 加入RequestMappingHandlerMapping @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { return new RequestMappingHandlerMapping(); } // ⬅️2. 继续加入RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapter @Bean public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() { TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver(); YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler(); MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter(); return handlerAdapter; } }
自定义RequestMappingHandlerAdapter
public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter { @Override public ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { return super.invokeHandlerMethod(request, response, handlerMethod); } }
public class A20 { private static final Logger log = LoggerFactory.getLogger(A20.class); public static void main(String[] args) throws Exception { AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class); // 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成 RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class); // 获取映射结果 Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods(); handlerMethods.forEach((k, v) -> { System.out.println(k + "=" + v); }); // 请求来了,获取控制器方法 返回处理器执行链对象 MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4"); request.setParameter("name", "张三"); request.addHeader("token", "某个令牌"); MockHttpServletResponse response = new MockHttpServletResponse(); HandlerExecutionChain chain = handlerMapping.getHandler(request); System.out.println(chain); System.out.println(">>>>>>>>>>>>>>>>>>>>>"); // HandlerAdapter 作用: 调用控制器方法 MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class); handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler()); // 检查响应 byte[] content = response.getContentAsByteArray(); System.out.println(new String(content, StandardCharsets.UTF_8)); } }
RequestMappingHandlerAdapter invokeHandlerMethod()方法 最终会去调用控制器
notion image

参数解析器

System.out.println(">>>>>>>>>>>>>>>>>>>>> 所有参数解析器"); for (HandlerMethodArgumentResolver resolver : handlerAdapter.getArgumentResolvers()) { System.out.println(resolver); }
notion image

自定义参数解析器

// 例如经常需要用到请求头中的 token 信息, 用下面注解来标注由哪个参数来获取它 // token=令牌 @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface Token { }
实现HandlerMethodArgumentResolver
public class TokenArgumentResolver implements HandlerMethodArgumentResolver { @Override // 是否支持某个参数 public boolean supportsParameter(MethodParameter parameter) { Token token = parameter.getParameterAnnotation(Token.class); return token != null; } @Override // 解析参数 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return webRequest.getHeader("token"); } }
设置自定义参数处理器
@Bean public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() { TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver(); YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler(); MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter(); handlerAdapter.setCustomArgumentResolvers(List.of(tokenArgumentResolver)); handlerAdapter.setCustomReturnValueHandlers(List.of(ymlReturnValueHandler)); return handlerAdapter; }
@PutMapping("/test3") public ModelAndView test3(@Token String token) { log.debug("test3({})", token); return null; }
 
// 请求来了,获取控制器方法 返回处理器执行链对象 MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3"); request.setParameter("name", "张三"); request.addHeader("token", "某个令牌"); MockHttpServletResponse response = new MockHttpServletResponse(); HandlerExecutionChain chain = handlerMapping.getHandler(request); System.out.println(chain); System.out.println(">>>>>>>>>>>>>>>>>>>>>"); // HandlerAdapter 作用: 调用控制器方法 MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class); handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler()); // 检查响应 byte[] content = response.getContentAsByteArray(); System.out.println(new String(content, StandardCharsets.UTF_8));
notion image

返回值解析器

System.out.println(">>>>>>>>>>>>>>>>>>>>> 所有返回值解析器"); for (HandlerMethodReturnValueHandler handler : handlerAdapter.getReturnValueHandlers()) { System.out.println(handler); }
notion image

自定义返回值处理器

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Yml { }
实现HandlerMethodReturnValueHandler 接口
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); } }
配置类在handlerAdapter里添加自定义返回值处理器
@Bean public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() { TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver(); YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler(); MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter(); handlerAdapter.setCustomArgumentResolvers(List.of(tokenArgumentResolver)); handlerAdapter.setCustomReturnValueHandlers(List.of(ymlReturnValueHandler)); return handlerAdapter;
@RequestMapping("/test4") @Yml public User test4() { log.debug("test4"); return new User("张三", 18); }
notion image