当前位置:   article > 正文

spring boot + spring mvc 原理解析_springboot springmvc原理

springboot springmvc原理

前言:spring mvc 是当前最为流行的一种java WEB 框架。在还没有spring boot以前,通常搭配tomcat等容器进行web项目的开发。而现在spring全家桶越来越完善。慢慢脱离来用容器来启动web项目。那么spring boot 搭配spring mvc的原理是什么。spring是怎么将url映射的具体的controller的。接下来,通过debug 方式一步步的去分析原理。

    spring boot提供来一种自动配置的方式来创建容器上下文,当创建web项目时添加spring-boot-starter-web来开启spring web的自动配置。当添加这个依赖,我们的依赖树如下:

 从上图中,我们可以看出,sping-boot-starter-web需要依赖

    1.spring-boot-starter 启动spring boot自动化配置

    2.hibernate-validator 提供一套spring mvc 参数校验的机制

    3.spring-mvc spring mvc核心组建

    4.spring-boot-starter-tomcat 提供内置的tomcat容器

    5.jackson-databind 在restfull的接口时惊醒对象和json的互转

通过上述依赖便可以创建一个web项目。

我们知道,spring项目核心思维就是控制反转,就是spring创建来一个上下文去管理容器中的对象或组件。

首先,先启动一个spring boot web项目。这个项目中只包含一个类

  1. @RestController
  2. @SpringBootApplication
  3. @RequestMapping("/")
  4. public class SpringbootwebApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(SpringbootwebApplication.class, args);
  7. }
  8. @Resource
  9. private ApplicationContext applicationContext;
  10. @RequestMapping("hello")
  11. public String hello(){
  12. return "success";
  13. }
  14. }

在application.properties中指定项目路径

server.servlet.context-path=/springboottest

启动成功用postman访问路径127.0.0.1:8080/springboottest/hello 。如果在return的时候打个断点,则我们可以看到debug工具中一下堆栈信息。我分为3部分

1.第一部分:接收请求

堆栈信息如下

接收请求
图1.接收请求

这里最重要的一个类是org.apache.tomcat.util.net.NioEndpoint。这里自行研究源码可以看出这其实是NIO的一个封装,

方法org.apache.tomcat.util.net.NioEndpoint#initServerSocket就是开启类一个NIO的ServerSocketChannel,并绑定对应地址,端口号。

  1. protected void initServerSocket() throws Exception {
  2. if (!getUseInheritedChannel()) {
  3. serverSock = ServerSocketChannel.open();
  4. socketProperties.setProperties(serverSock.socket());
  5. InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
  6. serverSock.socket().bind(addr,getAcceptCount());
  7. } else {
  8. // Retrieve the channel provided by the OS
  9. Channel ic = System.inheritedChannel();
  10. if (ic instanceof ServerSocketChannel) {
  11. serverSock = (ServerSocketChannel) ic;
  12. }
  13. if (serverSock == null) {
  14. throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
  15. }
  16. }
  17. serverSock.configureBlocking(true); //mimic APR behavior
  18. }

预估是sping,容器启动的时候执行类这个方法。这时候我们可以在这里打个断点,重新启动一下堆栈信息如下

 

NioEndpoint 启动过程
图2.NioEndpoint启动过程

 

根据堆栈信息,可以看出,在spring boot容器启动的时候,会同时启动org.springframework.boot.web.embedded.tomcat.TomcatWebServer,在tomcat中去启动NioEndpoint 开起监听。

监听到的客户端请求,这里需要了解一下7层网络协议的概念,socket是一种对传输层协议TCP/IP的封装。而http是更加面向用户的协议,著名的三次握手最终形成一个http请求。从图1.接收请求看出请求会经过一系列的Processor最终在org.apache.coyote.http11.Http11Processor中最终生成servlet的request和response。这个方法代码很多,就不复制上来类,主要的作用就是处理响应的一些状态如503,400,500.同时生成request和response,并封装超时时间。

在得到request和response之后后面的几个类是处理http的一些通用的请求头,或者响应头信息这里不说说明类,可以自己去看看。

可以看出,这里从监听到请求到形成http请求需要用的组件都是tomcat的apache包下面的组件

2.过滤链

在得到request之后,tomcat会处理一个过滤链org.apache.catalina.core.ApplicationFilterChain,用于统一处理,拦截请求。

图3.过滤链

 

这个流程比较简单。与传统tomcat是一样的。但是sping为我们默认添加链很多的Filter去处理业务。最终我们拿到HttpServletRequest和HttpServletResponse

这里用到的组件是tomcat的apache包下面的组件和spring 的filter

3.分发请求

图3.分发请求

分发请求过程用到的第一个类是org.springframework.web.servlet.DispatcherServlet 继承了Servlet类

图4.​DispatcherServlet结构图

 

DispatcherServlet的作用就是分发请求到对应的controller 。

引用源码的注释

  1. Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers
  2. or HTTP-based remote service exporters. Dispatches to registered handlers for processing
  3. a web request, providing convenient mapping and exception handling facilities.

百度翻译:

用于HTTP请求处理程序/控制器的中央调度器,例如用于WebUI控制器或者基于HTTP的远程服务导出器。发送给已注册处理程序以进行处理一个web请求,提供方便的映射和异常处理设施。

同时DispatcherServlet继承类Servlet,被ApplicationFilterChain持有。从tomcat完全过渡到spring web。

请求在dispatcherServlet最终被执行到org.springframework.web.servlet.DispatcherServlet#doService的方法。然后调用org.springframework.web.servlet.DispatcherServlet#doDispatch进行分发。

我们来看一下dispatcher中有什么

图5.dispatcherServlet 主要对象
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  6. try {
  7. ModelAndView mv = null;
  8. Exception dispatchException = null;
  9. try {
  10. processedRequest = checkMultipart(request);
  11. multipartRequestParsed = (processedRequest != request);
  12. // Determine handler for the current request.
  13. mappedHandler = getHandler(processedRequest);
  14. if (mappedHandler == null) {
  15. noHandlerFound(processedRequest, response);
  16. return;
  17. }
  18. // Determine handler adapter for the current request.
  19. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  20. // Process last-modified header, if supported by the handler.
  21. String method = request.getMethod();
  22. boolean isGet = "GET".equals(method);
  23. if (isGet || "HEAD".equals(method)) {
  24. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  25. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  26. return;
  27. }
  28. }
  29. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  30. return;
  31. }
  32. // Actually invoke the handler.
  33. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  34. if (asyncManager.isConcurrentHandlingStarted()) {
  35. return;
  36. }
  37. applyDefaultViewName(processedRequest, mv);
  38. mappedHandler.applyPostHandle(processedRequest, response, mv);
  39. }
  40. /**
  41. *****省略****
  42. **/
  43. }

再结合doDispatch源码。发现dispatcherServlet中维护来一系列的HandlerMapping.而我们controller中申明的requestMapping带在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping这个类中维护。doDispatch的工作就是在根据request中的url匹配到最合适的handler。这个handler就只对应controller的HandlerMethod。封装在HandlerExecutionChain的handler属性。同时handler还处理来拦截器对应的工作

  1. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  2. return;
  3. }
  4. // Actually invoke the handler.
  5. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  6. if (asyncManager.isConcurrentHandlingStarted()) {
  7. return;
  8. }
  9. applyDefaultViewName(processedRequest, mv);
  10. mappedHandler.applyPostHandle(processedRequest, response, mv);

 

applyPreHandle和applyPostHandle,例如applyPreHandle就是HandlerInterceptor的前置处理

  1. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HandlerInterceptor[] interceptors = getInterceptors();
  3. if (!ObjectUtils.isEmpty(interceptors)) {
  4. for (int i = 0; i < interceptors.length; i++) {
  5. HandlerInterceptor interceptor = interceptors[i];
  6. if (!interceptor.preHandle(request, response, this.handler)) {
  7. triggerAfterCompletion(request, response, null);
  8. return false;
  9. }
  10. this.interceptorIndex = i;
  11. }
  12. }
  13. return true;
  14. }

这个通过抓包很容易知道。除了HandlerMethod。spring还提供了HandlerAdapter机制去处理请求。这里我们最主要关注RequestMappingHandlerAdapter这个,因为它是是一般用来处理HandlerMethod的。

从图3可知请求会经过org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

在这个栈桢,以及后面的几个分别做来session校验,参数处理(argumentResolvers),数据绑定,通过java放射执行HandlerMethod中的方法,返回参数处理(returnValueHandlers),请求缓存等。

 

最终请求到来Controller中。

这里用到的组件是spring的mvc包下面的组件和

以上就是完整的spring boot + spring mvc 请求的流程。最好的学习方式就是看源码,通过debug进行查看每一个流程。并有选择性的关注某几个具体流程。spring 还为我们做来很多事情。例如跨域CORS ,session管理等。其中一些配置可以实现org.springframework.web.servlet.config.annotation.WebMvcConfigurer类来配置。

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/222920
推荐阅读
相关标签
  

闽ICP备14008679号