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对象的情况

相关推荐
2301_805962938 小时前
嘉立创EDA添加自己的元件和封装
java·开发语言
Rookie_explorers8 小时前
go私有仓库athens搭建
开发语言·后端·golang
TimberWill8 小时前
MinIO整合SpringBoot实现获取文件夹目录结构及文件内容
java·linux·springboot
傻啦嘿哟8 小时前
Python爬虫进阶:反爬机制突破与数据存储实战指南
开发语言·爬虫·python
2301_764441338 小时前
基于Streamlit构建的风水命理计算器
开发语言·python
崎岖Qiu8 小时前
【设计模式笔记18】:并发安全与双重检查锁定的单例模式
java·笔记·单例模式·设计模式
曲莫终8 小时前
spring.main.lazy-initialization配置的实现机制
java·后端·spring
❀͜͡傀儡师8 小时前
docker部署Docker Compose文件Web管理工具Dockman
java·前端·docker·dockman
沐雪架构师8 小时前
大模型Agent面试精选题(第五辑)-Agent提示词工程
java·面试·职场和发展
智航GIS8 小时前
1.2 python及pycharm的安装
开发语言·python·pycharm