前言: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项目。这个项目中只包含一个类

@RestController
@SpringBootApplication
@RequestMapping("/")
public class SpringbootwebApplication {public static void main(String[] args) {SpringApplication.run(SpringbootwebApplication.class, args);}@Resourceprivate ApplicationContext applicationContext;@RequestMapping("hello")public String hello(){return "success";}
}

在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,并绑定对应地址,端口号。

protected void initServerSocket() throws Exception {if (!getUseInheritedChannel()) {serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());serverSock.socket().bind(addr,getAcceptCount());} else {// Retrieve the channel provided by the OSChannel ic = System.inheritedChannel();if (ic instanceof ServerSocketChannel) {serverSock = (ServerSocketChannel) ic;}if (serverSock == null) {throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));}}serverSock.configureBlocking(true); //mimic APR behavior}

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

图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 。

引用源码的注释

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllersor HTTP-based remote service exporters. Dispatches to registered handlers for processinga 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 主要对象
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}/*******省略******/}

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

if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);

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

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;}

这个通过抓包很容易知道。除了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类来配置。

spring boot + spring mvc 原理解析相关推荐

  1. Spring Boot(18)---启动原理解析

    Spring Boot(18)---启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会 ...

  2. Spring Boot: SpringFox Swagger原理解析及简单实用

    文章目录 简介 一.Swagger简单使用 二.Swagger原理 三.Swagger架构分析及组成 其他 简介 API的全称是应用编程接口(Application Programming Inter ...

  3. Spring Boot自动装配原理详解

    目录 1.环境和依赖 1.1.spring boot版本 1.2.依赖管理 2.自动装配 2.1.流程概述 2.2.三大步前的准备工作 2.2.1.注解入口 2.2.2.获取所有配置类 2.3.获取过 ...

  4. 在controller中调用指定参数给指定表单_第005课:Spring Boot 中MVC支持

    Spring Boot 的 MVC 支持主要介绍实际项目中最常用的几个注解,包括 @RestController. @RequestMapping.@PathVariable.@RequestPara ...

  5. Spring Boot进阶:原理、实战与面试题分析

    在当下的互联网应用中,业务体系日益复杂,业务功能也在不断地变化.以典型的电商类应用为例,其背后的业务功能复杂度以及快速迭代要求的开发速度,与5年前的同类业务系统相比,面临着诸多新的挑战. 这些挑战中核 ...

  6. Spring Boot Spring MVC 异常处理的N种方法

    默认行为 根据Spring Boot官方文档的说法: For machine clients it will produce a JSON response with details of the e ...

  7. Spring Boot自动配置原理、实战

    Spring Boot自动配置原理 Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射. org.s ...

  8. Spring Boot Spring MVC 异常处理的N种方法 1

    github:https://github.com/chanjarste... 参考文档: Spring Boot 1.5.4.RELEASE Documentation Spring framewo ...

  9. 芋道 Spring Boot 自动配置原理

    转载自  芋道 Spring Boot 自动配置原理 1. 概述 友情提示:因为本文是分享 Spring Boot 自动配置的原理,所以需要胖友有使用过 Spring Boot 的经验.如果还没使用过 ...

最新文章

  1. 肝了一个月,终于搞到了 30 页的 Python 进阶面试题
  2. Spark1.x和2.x如何读取和写入csv文件
  3. linux批量杀进程_Linux下批量杀JAVA进程
  4. Vue项目 全局定义日期时间方法处理
  5. spring+springmvc+mybatis配置
  6. KNN-K最近邻算法+实例应用
  7. android 仿QQ手机版
  8. python与pexpect实现ssh操作
  9. python处理excel表格-60万行的Excel数据,Python轻松处理
  10. JS 生成英文字母 A-Z
  11. httpclient 无信任证书使用https
  12. Delphi10.4.1开发Linux应用视频重播
  13. MES系统源码 MES系统功能介绍
  14. 【毕业求职季】-听说你想去大厂看学妹,带你看看字节广告运营岗面试长啥样?
  15. shell编程三大神器之grep
  16. QML 中如何动态创建组件
  17. 汇千网-五年后,我们能用脑机接口做什么?
  18. 设置BIOS从USB启动!
  19. 冰河公开了常逛的23大学习网站,赶快收藏
  20. python学习笔记(七):运算符和流程控制

热门文章

  1. 计算机网络——阻塞IO、非阻塞IO、IO多路复用
  2. Python绘图实例23:彩色房子绘制
  3. 数据结构与算法——绪论
  4. 世界上最好的双马尾写的博客
  5. Hadoop相关组成介绍
  6. Revit中阀门在项目中不可用无法与管道连接?
  7. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了)
  8. android手机量体温,手机体温测量app
  9. 【torch安装踩雷】torchtext、torch、anaconda安装问题解决
  10. 搞个这样的APP要多久