3. HTTP 请求在 Web 应用中的处理流程
在穿越了 Web 容器之后,HTTP 请求将被投送到 Web 应用,我们继续以 Tomcat 为例剖析后续流程。Web 容器与 Web 应用的衔接是通过配置文件 web.xml 完成的。web.xml 是遵循 Java Servlet 标准规范的配置文件,我们通过这份配置文件定义构成 Web 应用的各种核心组件和初始化配置,其中包括:过滤器 Filter、监听器 Listener、伺服器 Servlet 等等。不同组件分别承担不同的功能,在介绍 Web 应用处理 HTTP 请求流程之前,我们照例先来了解一下这些核心组件。
3.1 Web 应用核心组件简介
3.1.1 过滤器 Filter
过滤器 Filter 负责对 HTTP 请求做预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对响应进行后处理。从 HTTP 请求的处理过程来看,Filter 主要参与以下几个环节:
- 在 HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest。
- 根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 报文头和数据。
- 在 Servlet 生成的 HttpServletResponse 抵达客户端之前,拦截 HttpServletResponse。
- 根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse 报文头和数据。
过滤器映射 filter-mapping,用于声明 Web 应用将会用到的过滤器,过滤器可被映射到一个 Servlet 或 URL 模式。如果将过滤器映射到一个 Servlet 上,那它就作用于特定 Servlet。如果将过滤器映射到一个 URL 模式,那么它将作用于任何资源,只要该资源的 URL 与 URL 模式匹配。如果对某个资源的请求匹配到多个 Filter,那么在处理 HTTP 请求过程中,Tomcat 将按照过滤器映射 filter-mapping 在配置文件 web.xml 中的先后顺序来执行,在前面的先执行,在后面的后执行,多个过滤器 Filter 可以组成调用链。
URL 模式匹配有三种类型规则:
- 精确匹配:如“/foo.htm”,那只会匹配“foo.htm”这个 URL。
- 路径匹配:如“/foo/*”,那只会匹配以 foo 为前缀的 URL。
- 后缀匹配:如“*.htm”,那只会匹配所有以“.htm”为后缀的 URL。
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.1.2 监听器 Listener
监听器 Listener 主要用于监听 Application、Session、Request 等对象的变化,每当这些对象发生变化就会回调用对应的监听方法。例如:在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。当 Servlet 容器启动或终止Web 应用时,会触发 ServletContextEvent 事件,该事件由 ServletContextListener 来处理。
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
3.1.3 伺服器 Servlet
伺服器 Servlet 负责处理客户端访问动态资源的 HTTP 请求,接口 javax.servlet.Servlet 定义了所有 Servlet 必须要实现的方法。
方法名称 | 功能说明 |
---|---|
destroy() | 由 Servlet 容器调用,用于关闭停止 Servlet 提供的服务 |
getServletConfig() | 获取 Servlet 初始化和启动时参数的配置信息对象 ServletConfig |
getServletInfo() | 获取 Servlet 的说明信息,包括:作者、版本和版权等等 |
init() | 由 Servlet 容器调用,籍由配置 ServletConfig 完成 Servlet 初始化,启动对外服务 |
service() | 由 Servlet 容器调用,让 Servlet 处理某个 HTTP 请求 |
从 HTTP 请求的处理过程来看,伺服器 Servlet 主要参与以下几个环节:
- 接收请求:客户端请求会被封装成 HttpServletRequest 对象,包含报文头参数和报文体等信息。
- 处理请求:通常调用 Servlet 的方法 service、doPost 或 doGet 等方法处理请求,并进一步调用业务层相应逻辑对其进行处理等。
- 反馈响应:处理完请求后,可以转发(forward)、重定向(redirect)到某个视图页面或者直接返回结果数据,转发是 HttpServletRequest 的方法,重定向是 HttpServletResponse 的方法。
在老兵哥的读书年代,Web 应用相对简单,主要是各种信息管理系统。当时 Spring 尚未诞生,主流技术栈是 JSP/Servlet,老兵哥我开发 Web 应用时主要编写继承自 HttpServlet 子类,将各种业务逻辑功能分别交由不同的 HttpServlet 子类实现。HttpServlet 继承自 GenericServlet,后者实现了接口 Servlet。
随着数字化和互联网化的不断推进,业务系统变得越来越复杂,HttpServlet 子类越写越多,web.xml 配置文件越来越复杂,这导致系统的扩展维护越来越困难,手工作坊式的开发方法已经跟不上业务发展的步伐了。
时势造英雄,Spring 就是在这种背景下呼之而出的,它创造性地发明了控制反转 IOC 和面向切面编程 AOP,极大地降低复杂度。如下面配置示例所示,整个 Web 应用只需要配置一个 Servlet 就可以了,它就是 Spring 的前置分发器 DispatcherServlet:
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/api</url-pattern>
</servlet-mapping>
3.2 Web 应用处理 HTTP 请求的流程
如下图所示,Web 应用处理 HTTP 请求的流程主要是穿越监听器 Listener 和过滤器链 Filters,最终抵达伺服器 Servlet 的过程:
原先我们将 Web 应用的复杂度直接暴露给了 Tomcat,现在 Spring 通过控制反转 IOC 和面向切面编程 AOP 等新技术接管了 Web 应用的复杂度。如前面 Servlet 的配置示例,整个 Web 应用只需要配置 Spring 提供的前置分发器 DispatcherServlet,开发者无需再编写和配置 HttpServlet 子类,所有业务逻辑功能将按照 Spring 的标准规范来开发,从原先的编写 Listener\Filter\Servlet 改为编写 Spring Component,包括:Controller、Service、Repository 等。
Tomcat 在接收到某个 Web 应用的 Http 请求之后,它会将所有请求都转交给前置分发器 DispatcherServlet,再由 DispatcherServlet 将请求派发给具体业务逻辑进行处理。DispatcherServlet 就是从 HttpServlet 派生的子类,它的类图关系如下所示:
3.3 Web 应用架构演进过程解析
Web 应用架构的演进过程就像创业孵化过程,最初创业团队打造的产品很简单,大家采用手工作坊模式来快速打造最小化可行产品,这时候团队的组织架构也跟产品一样扁平简单。但随着产品被越来越多的用户使用,功能变得越来越复杂,接着必须引进新架构才能有效管理复杂度,同时团队规模的扩展也需要与业务发展匹配的组织架构,这样才能保证产品的不断发展。
Web 应用架构的演化过程跟 Tomcat 体系结构的形成过程类似,老兵哥会经常借助“俄罗斯套娃”这个模型来阐述架构,Web 容器和 Web 应用这两层的架构原则是类似的,就像大娃娃套着小娃娃一样。
Spring 的 IOC 容器跟 Tomcat 的 Servlet 容器类似,也是通过配置文件等方式来定义组件,然后在启动过程中将这些定义好的组件初始化并添加到容器当中,后续使用时从容器查找获取。早期 Web 应用主要由大量开发者编写的 Filter\Listener\Servlet 等组件构成,这些核心组件的配置全部都通过 web.xml 配置文件来维护,那么 Web 应用和 Web 容器之间其实不是松耦合的,而引进 Spring 之后就变成只需要配置 DispatcherServlet 等少量组件了,达到了分层架构的要求,更加有利于开发复杂的 Web 应用。如果采用架构的专业术语来描述,这就是经典的分层架构模式,层与层之间松耦合,仅通过少量的接口衔接,每层内部高内聚。
本文主要价值是帮助大家梳理出端到端的全流程框架,也就是我们常说的全局视角或者上帝视角。有了这个框架之后,我们可以根据自己的需要按图索骥找相关节点的资料来研究学习,不至于陷入细节找不到方向。当然,考虑到我们每个人的工作学习情况不同,平时遇到的问题也不同,本文内容无法覆盖所有人遇到的问题,欢迎大家留言提问。今天先分享到这里,如果你觉得有价值,麻烦动动手指 转发 给其他需要的小伙伴。另外,老兵哥我后续还会分享职业规划、应聘面试、技能提升、影响力打造等经验,关注「 IT老兵哥 」,赋能程序人生!
本系列其他文章索引如下:
- 图解 Spring:HTTP 请求的处理流程与机制【1】
- 图解 Spring:HTTP 请求的处理流程与机制【2】
- 图解 Spring:HTTP 请求的处理流程与机制【4】
- 图解 Spring:HTTP 请求的处理流程与机制【5】
图解 Spring:HTTP 请求的处理流程与机制【3】的更多相关文章
-
图解 Spring:HTTP 请求的处理流程与机制【2】
2. HTTP 请求在 Web 容器中的处理流程 Web 容器以进程的方式在计算机上运行,我们知道进程是系统资源分配的最小单元,线程是系统任务执行的最小单元.从这个角度看,Web 容器就像是邮包收件人 ...
-
图解 Spring:HTTP 请求的处理流程与机制【1】
2003 年,老兵哥初到中兴开始研究生实习,Spring 就是那年诞生的,2004 年 3 月发布了 1.0 版本,到现在已经超过 15 年了.从单体式分层架构到云原生微服务架构,它稳坐在 JAVA ...
-
图解 Spring:HTTP 请求的处理流程与机制【4】
4. HTTP 请求在 Spring 框架中的处理流程 在穿越了 Web 容器和 Web 应用之后,HTTP 请求将被投送到 Spring 框架,我们继续剖析后续流程.Web 应用与 Spring M ...
-
图解 Spring:HTTP 请求的处理流程与机制【5】
5. HTTP 请求处理相关配置文件说明 HTTP 请求穿越的整个空间是分层的,包括:Web 容器.Web 应用.Spring 框架等,它们每层都是通过配置文件配置初始化的,这是一种松耦合的架构设计. ...
-
Spring Security 概念基础 验证流程
Spring Security 概念基础 验证流程 认证&授权 认证:确定是否为合法用户 授权:分配角色权限(分配角色,分配资源) 认证管理器(Authentication Manager) ...
-
描述Spring Web MVC的工作流程
Spring Web MVC的共工作流程如下: 1.浏览器发出Spring mvc请求,请求给前端控制器 DispatcherServlet处理. 2.控制器通过HandlerMapping维护的请求 ...
-
Spring Security 的注册登录流程
Spring Security 的注册登录流程 数据库字段设计 主要数据库字段要有: 用户的 ID 用户名称 联系电话 登录密码(非明文) UserDTO对象 需要一个数据传输对象来将所有注册信息发送 ...
-
图解classloader加载class的流程及自定义ClassLoader
图解classloader加载class的流程及自定义ClassLoader 博客分类: JVM JavaJVM虚拟机EXTSUN /** * 转载请注明作者longdick http://l ...
-
web请求的处理流程
web请求的处理流程如下: 1.客户发起请求到服务器网卡:2.服务器网卡接受到请求后转交给内核处理:3.内核根据请求对应的套接字,将请求交给工作在用户空间的Web服务器进程4.Web服务器进程根据用户 ...
随机推荐
-
python04 面向对象编程02
为啥要用类而不用函数呢 记住两个原则: 减少重复代码 代码会经常变更 2 会对变量或字符串的合法性检测(在实例初始化的时候能够统一初始化各个实例的变量,换做函数来说,要弄出同样的变量那么在初始化 ...
-
ATT 解锁手机
最近豪们都在忙着买买买950 本土鳖左瞅瞅右看看实在钱包不够豪 正好看到美帝640打折39刀 (http://www.microsoftstore.com/store/msusa/en_US/pdp/ ...
-
[Everyday Mathematics]20150224
设 $A,B$ 是 $n$ 阶实对称矩阵, 它们的特征值 $>1$. 试证: $AB$ 的特征值的绝对值 $>1$.
-
HTML5 indexedDB数据库的入门学习(一)
笔者早些时间看过web sql database,但是不再维护和支持,所以最近初步学习了一下indexedDB数据库,首先indexedDB(简称IDB)和web sql database有很大的差别 ...
-
定时工作方式2实现1s定时
定时器的几种工作方式中,除0和前面的1类似都需要初始化计数值,然后开始计数,计数溢出后,需要再次控制计数的初值,但工作模式2不同于此,工作方式2将THx和TLx分开处理,将初值存放在THx中,计数时只 ...
-
c++多线程同步使用的对象
线程的同步 Critical section(临界区)用来实现“排他性占有”.适用范围是单一进程的各线程之间.它是: · 一个局部性对象,不是一个核心对象. · 快速而 ...
-
c++针对数据库,文件的操作总结(原始)
1.将文件保存到sqlserver数据库的相关操作: Update t1 .txt’, SINGLE_BLOB ) Select convert( varchar(), data ) 注:fileTy ...
-
【排序算法】直接选择排序算法 Java实现
基本思想 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后. 初始状态:无序区为a[1...n],有序区为空. 第一次排序:在无序区a[1...n]中选出最小的记录a[k],将它与有序区的第 ...
-
swift -- 类中的方法
一. 引用类型 类 在类中定义方法 class Person { //属性 var name : String = "" //方法 //实例方法 : 在类里面创建一个方法 fun ...
-
关于<;Servlet>;定义
1,百度百科定义: Servlet,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容 ...