SpringMVC底层流程解析

SpringBoot底层还是通过SpringMVC来解析请求的,SpringMvc中最核心的东西就是DispatcherServlet,Springboot启动过程中会先启动tomcat,然后创建DispatcherServlet,去调用里面的init方法

,会创建一个Spring容器,并且添加一个ContextRefreshListener监听器,该监听器会监听ContextRefreshedEvent事件(Spring容器启动完成后就会发布这个事件),也就是说Spring容器启动完成后,就会执行ContextRefreshListener中的onApplicationEvent()方法,从而最终会执行DispatcherServlet中的initStrategies()。

复制代码
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   //HandlerMapping和适配器
   initHandlerMappings(context);
   /适配器就相当于一个接口有多个继承类,在继承类里面重写自己的逻辑,方便做扩展,比如自己去重定义HandlerMapping
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}

其中最为核心的就是HandlerMapping和HandlerAdapter。

Handler(Handler表示请求处理器,在SpringMVC中有四种Handler):

1.实现了Controller接口的Bean对象

2.实现了HttpRequestHandler接口的Bean对象

3.添加了@RequestMapping注解的方法

4.一个HandlerFunction对象

最常见的也是第三种,在Controller层上面去添加RequestMapping方法,添加路径,定位到业务方法中

HandlerMapping(HandlerMapping负责去寻找Handler,并且保存路径和Handler之间的映射关系)

1.RequestMappingHandlerMapping:负责@RequestMapping的方法(只找一种,剩下几种不常用,就不写了)

RequestMappingHandlerMapping寻找HandlerMapping的流程

1.找出Spring容器中所有beanType

2.判断beanType是不是有@Controller注解,或者是不是有@RequestMapping注解

3.判断成功则继续找beanType中加了@RequestMapping的Method

4.并解析@RequestMapping中的内容,比如method、path,封装为一个RequestMappingInfo对象

5.最后把RequestMappingInfo对象做为key,Method对象封装为HandlerMethod对象后作为value,存入registry中

6.registry就是一个Map

RequestMappingHandlerMapping的 父类AbstractHandlerMapping

AbstractHandlerMapping会负责调用子类的getHandlerInternal(HttpServletRequest request)方法从而找到请求对应的Handler,然后AbstractHandlerMapping负责将Handler和应用中所配置的HandlerInterceptor整合成为一个HandlerExecutionChain对象。

选择哪个HandlerMapping?

当DispatcherServlet接受到请求的时候,会遍历所有的HandlerMapping,找到第一个(前面都说了有多个HandlerMapping,只是这边只写了一个,BeanNameUrlHandlerMapping这个的优先级是最高的)然后去寻找对应的Handler

HandlerAdapter(适配器模式)

1.添加了@RequestMapping注解的方法,具体为一个HandlerMethod,执行的就是当前加了注解的方法

2.因为有多种HandlerMapping,如果没有这种适配器模式的话,你就得写很多的if elseif elseif(什么什么Handler)来实现他的逻辑方法,所以搞了这个东西,相当于一个接口有多种实现类,在自己的实现类里面写自己的逻辑

复制代码
public boolean supports(Object handler) {
   return (handler instanceof HttpRequestHandler);
}

public boolean supports(Object handler) {
   return (handler instanceof Controller);
}

public final boolean supports(Object handler) {
   return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

public boolean supports(Object handler) {
   return handler instanceof HandlerFunction;
}
谁支持就适配谁,然后调用对应的handle方法


SpringMvc的解析逻辑
复制代码
   1.RequestParamMethodArgumentResolver:负责处理@RequestParam
   2.RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
   3.SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
   4.RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
   5.RequestResponseBodyMethodProcessor:负责处理@RequestBody
   6.。。。。还有很多这样的注解

而在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,也是很粗暴

复制代码
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {

   HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
   if (result == null) {
      for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
         //就是遍历所有的HandlerMethodArgumentResolver,哪个能支持处理当前这个参数就由哪个处理。
         if (resolver.supportsParameter(parameter)) {
            result = resolver;
            this.argumentResolverCache.put(parameter, result);
            break;
         }
      }
   }
   return result;

}

@RequestMapping方法返回值解析

而方法返回值,也会分为不同的情况。比如有没有加@ResponseBody注解,如果方法返回一个String:

1.加了@ResponseBody注解:表示直接将这个String返回给浏览器

2.没有加@ResponseBody注解:表示应该根据这个String找到对应的页面,把页面返回给浏览器

在SpringMVC中,会利用HandlerMethodReturnValueHandler来处理返回值:

1.RequestResponseBodyMethodProcessor:处理加了@ResponseBody注解的情况

2.ViewNameMethodReturnValueHandler:处理没有加@ResponseBody注解并且返回值类型为String的情况

3.ModelMethodProcessor:处理返回值是Model类型的情况

4.还有很多其他的...

RequestResponseBodyMethodProcessor 相当于会把方法返回的对象直接响应给浏览器 ,如果返回的是一个字符串,那么好说,直接把字符串响应给浏览器,那如果返回的是一个Map呢?是一个User对象呢?该怎么把这些复杂对象响应给浏览器呢?

处理这块,SpringMVC会利用HttpMessageConverter来处理,比如默认情况下,SpringMVC会有4个HttpMessageConverter:

1.ByteArrayHttpMessageConverter:处理返回值为字节数组的情况,把字节数组返回给浏览器

2.StringHttpMessageConverter:处理返回值为字符串的情况,把字符串按指定的编码序列号后返回给浏览器

3.SourceHttpMessageConverter:处理返回值为XML对象的情况,比如把DOMSource对象返回给浏览器

4.AllEncompassingFormHttpMessageConverter:处理返回值为MultiValueMap对象的情况

相关推荐
heartbeat..40 分钟前
介绍java中常用于处理 Excel 文件的Apache POI
java·apache·excel·poi
路边草随风40 分钟前
java 实现 flink 读 kafka 写 iceberg
java·flink·kafka
路边草随风42 分钟前
java 实现 flink cdc 读 mysql binlog 按表写入kafka不同topic
java·大数据·mysql·flink
低客的黑调42 分钟前
Spring MVC 全面详解:原理、组件、实战与高级特性
java·spring·mvc
STARFALL00142 分钟前
spring mvc 自定义Converter 设置
java·spring·mvc
java_logo43 分钟前
Jenkins Docker 容器化部署指南
java·运维·servlet·docker·容器·jdk·jenkins
♡喜欢做梦1 小时前
MyBatis操作数据库(进阶):动态SQL
java·数据库·sql·java-ee·mybatis
lusasky1 小时前
com.itextpdf堆外内存(Off-Heap Memory)泄露
java
.豆鲨包1 小时前
【Android】深入理解Window和WindowManager
android·java