【第二十讲】
RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter
DispatcherServlet
初始化时机DispatcherServlet
初始化都做了什么RequestMappingHandlerMapping
基本用途RequestMappingHandlerAdapter
基本用途- 自定义参数和返回值处理器
文章目录
启动容器
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 映射
- 调用控制器方法、并处理方法参数与方法返回值
-
DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化
-
在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等,并逐一调用它们的初始化
-
RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中
- key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
- value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
- 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
-
RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:
- HandlerMethodArgumentResolver 解析控制器方法参数
- HandlerMethodReturnValueHandler 处理控制器方法返回值