1.1_Java相关概念
Java内存马学习笔记–基本知识1
网络安全是数字时代的基石,但学习过程中必须严守法律红线。
根据《中华人民共和国网络安全法》《数据安全法》等法律法规,任何未经授权的网络测试、数据访问或攻击行为均属违法。本文所有技术讨论与实例均基于合法授权的靶场环境(如Metasploitable、DVWA、Hack The Box等),严禁将文中方法应用于真实系统或非授权场景。
网络安全学习应以提升防御能力为目标,而非成为攻击工具。
写在前边
在 Java 生态里,“容器”并不一定指最顶层、最完整的独立服务器,而是泛指 “负责管理一组对象的生命周期、为其提供运行环境、配置和服务的框架或模块”。所以 “容器”是一种角色,而不是对物理规模的限定。哪怕它寄生在另一个更大的容器内,只要它具备“管理一组子部件的生命周期和运行环境”的功能,就可以独立被称作容器。
一、Servlet容器
特别注意:Servlet容器与Servlet 不是同一个东西, Servlet 是 Tomcat 提供的用于处理 Web 请求的标准编程接口(API)
Servlet容器是管理Java Servlet生命周期并处理HTTP请求的运行环境,它为Servlet 提供加载、初始化、执行和销毁的完整支持,是Java Web应用运行的核心。
什么是 Servlet 容器
Servlet容器(如Tomcat、Jetty)是一个符合Java EE(现Jakarta EE)规范的服务器程序,专门用于运行Servlet和JSP。它本质上是一个“管家”,负责:
- 接收客户端的HTTP请求
- 将请求封装成标准的
ServletRequest对象 - 调用对应的Servlet进行处理
- 将处理结果通过
ServletResponse返回给客户端
Servlet容器的核心作用
- 生命周期管理
容器控制Servlet的整个生命周期:- **init()**:在Servlet首次加载时调用,完成初始化
- **service()**:每次请求到来时,容器调用此方法处理请求
- **destroy()**:应用关闭时释放资源
- 请求分发与路由
容器根据URL映射(<url-pattern>)找到对应的Servlet,并将请求转发给它处理。例如,访问/hello,容器会调用HelloServlet的service()方法。 - 多线程支持
每个请求由独立线程处理,容器自动管理线程池,实现高并发响应。 - 与Web服务器集成
容器通常内嵌于Web服务器(如Tomcat同时是Web服务器和Servlet容器),直接监听端口(如8080),接收HTTP请求并处理动态内容。 - 支持JSP与会话管理
容器将JSP页面编译为Servlet执行,并提供HttpSession机制实现用户会话跟踪。
常见的 Servlet 容器
表格
| 容器 | 类型 | 特点 |
|---|---|---|
| Apache Tomcat | 轻量级 | 最流行,Spring Boot默认内嵌 |
| Jetty | 轻量级 | 启动快,适合嵌入式应用 |
| Undertow | 轻量级 | 高性能,WildFly默认容器 |
| WebLogic / WebSphere | 重量级 | 支持完整Java EE,企业级应用 |
💡 在Spring Boot中,默认使用内嵌Tomcat,开发者无需单独部署服务器,一键启动即可运行Web应用。
二、Tomcat应用服务器
Tomcat 是一个开源的 Java Web 应用服务器,主要用于运行 Java 编写的 Web 应用程序,特别是基于 Servlet 和 JSP 技术的项目。它由 Apache 软件基金会维护,是开发者最常用的轻量级应用服务器之一。
Tomcat 的核心作用
- 执行 Servlet 与 JSP
- 当你访问一个由 Java 开发的网站(如电商后台、管理系统),请求会发送到 Tomcat。
- Tomcat 负责加载并执行对应的 Servlet,将动态内容生成 HTML 页面返回给浏览器。
- 对于 JSP(JavaServer Pages)文件,Tomcat 会先将其编译成 Servlet,再执行。
- 处理 HTTP 请求
- Tomcat 内置了 HTTP 服务器功能,可以直接监听 8080 等端口,接收浏览器的请求。
- 它能解析 HTTP 协议,封装请求为
HttpServletRequest对象,并调用相应组件处理。
- 支持多种协议与 I/O 模型
- 支持 HTTP/1.1、HTTP/2 和 AJP 协议,可用于与 Nginx/Apache 集成。
- 提供 NIO、NIO.2 和 APR 等 I/O 模型,提升高并发下的性能表现。
- 类加载隔离
- 每个部署的应用都有独立的类加载器(ClassLoader),避免不同项目之间的依赖冲突。
- 热部署支持
- 在开发过程中,修改代码后可设置
reloadable="true"实现自动重启,无需手动停止服务。
- 在开发过程中,修改代码后可设置
- **嵌入式使用(如 Spring Boot)**
- 现代 Java 项目(如 Spring Boot)常将 Tomcat “内嵌”进应用中,打包成 jar 文件直接运行,无需独立安装服务器。
典型应用场景
| 场景 | 说明 |
|---|---|
| 开发调试 | 开发者本地运行 Web 项目,快速测试接口和页面 |
| 中小型系统部署 | 企业内部系统、CRM、OA 等常用 Tomcat 部署 |
| 微服务架构 | Spring Cloud 微服务默认使用内嵌 Tomcat 处理 HTTP 请求 |
| 与反向代理配合 | Nginx 处理静态资源,Tomcat 专注动态请求,提升整体性能 |
三、tomcat与servlet容器的关系
**tomcat 本质上就是一个 Servlet 容器。**
在 Java Web 开发体系中,这两个概念经常互换使用,但侧重点略有不同:Servlet 容器是一个抽象的功能角色(负责运行和管理 Servlet),而 Tomcat 是这一角色的具体实现产品之一。
1、 核心定义与关系
- **Servlet 容器(Servlet Container)**:
- 定义:它是 Web 服务器或应用服务器的一部分,专门用于管理 Servlet (小程序)的生命周期(加载、初始化、服务、销毁),并将客户端的请求映射到相应的 Servlet 上。
- 作用:它为 Servlet 提供运行环境,屏蔽了底层的网络通信细节(如 TCP/IP、HTTP 协议解析),让开发者只需关注业务逻辑。
- 其他实现:除了 Tomcat,常见的 Servlet 容器还有 Jetty、Undertow、WebLogic、WebSphere 等。
- Tomcat:
- 定义:Apache 软件基金会开发的一个开源、轻量级的 Web 应用服务器。
- 身份:它既是一个 Web 服务器(可以处理静态资源如 HTML、图片),也是一个 Servlet 容器(可以运行动态的 Java Servlet 和 JSP)。
- 地位:它是目前最流行的 Servlet 容器实现,也是 Spring Boot 等现代框架默认内嵌的容器。
总结:Tomcat 是 Servlet 规范的具体落地产品。你可以说“Tomcat 是一个 Servlet 容器”,但不能说“所有 Servlet 容器都是 Tomcat”。
2、Tomcat 作为 Servlet 容器的工作原理
Tomcat 内部通过核心组件协作来实现 Servlet 容器的功能,主要流程如下:
2.1 核心组件架构
Tomcat 的容器部分采用分层架构,从大到小依次为:
- **Engine(引擎)**:最高级别的容器,代表整个 Tomcat 服务。
- **Host(主机)**:代表一个虚拟主机(通常对应一个域名)。
- **Context(上下文)**:代表一个具体的 Web 应用程序(即一个 WAR 包或一个项目目录)。
- **Wrapper(包装器)**:代表一个具体的 Servlet 实例。它是容器的最小单元,负责管理单个 Servlet 的生命周期。
2.2 请求处理流程
当浏览器发起一个 HTTP 请求时,Tomcat 作为 Servlet 容器的处理步骤如下:
- **接收请求(Connector)**:
- 连接器监听端口(如 8080),接收 TCP 连接。
- 解析 HTTP 协议,将字节流转换为 Tomcat 内部的
Request对象。
- **映射路由(Mapper)**:
- 根据请求的 URL(如
/api/user),在容器层级结构中找到对应的Context(哪个项目)和Wrapper(哪个 Servlet)。
- 根据请求的 URL(如
- **调用 Servlet(Container)**:
- 容器创建标准的
HttpServletRequest和HttpServletResponse对象。 - 如果 Servlet 尚未初始化,容器会加载类、创建实例并调用
init()方法。 - 容器调用 Servlet 的
service()方法,传入请求和响应对象。
- 容器创建标准的
- 执行业务逻辑:
- Servlet 执行开发者编写的代码(如查询数据库、计算数据)。
- Servlet 将结果写入
HttpServletResponse。
- 返回响应:
- 容器获取响应对象中的数据。
- 连接器将其转换为 HTTP 响应报文,发送回客户端。
3、Servlet 容器管理的核心职责
Tomcat 作为容器,为 Servlet 提供了以下关键支持,使得开发者无需关心底层细节:
| 职责 | 说明 |
|---|---|
| 生命周期管理 | 自动处理 Servlet 的 init()(初始化)、service()(服务)、destroy()(销毁)。默认情况下,Servlet 是单例的,多线程共享同一个实例。 |
| 多线程支持 | 为每个请求分配独立的线程,确保高并发下的处理能力,同时保证线程安全(需开发者注意共享变量的同步)。 |
| 请求封装 | 将原始的 HTTP 字节流解析并封装成易用的 HttpServletRequest 对象,提供 getParameter()、getHeader() 等方法。 |
| 响应封装 | 提供 HttpServletResponse 对象,简化数据写入、状态码设置和头信息管理。 |
| 类加载隔离 | 每个 Web 应用(Context)有独立的类加载器,避免不同项目间的 Jar 包冲突。 |
4、常见误区澄清
- **误区 1:Tomcat 只是 Servlet 容器?**
- 纠正:Tomcat 既是 Web 服务器(处理静态文件),也是 Servlet 容器(处理动态 Java 代码)。但在高性能场景下,常前置 Nginx 处理静态资源,Tomcat 专注作为 Servlet 容器处理动态请求。
- **误区 2:Servlet 可以直接运行?**
- 纠正:Servlet 只是一个接口规范(Java 类),它不能独立运行。必须部署在像 Tomcat 这样的 Servlet 容器中,由容器启动 JVM 并调用它。
- **误区 3:Spring Boot 没有 Tomcat?**
- 纠正:Spring Boot 默认内嵌了 Tomcat(作为 Servlet 容器)。当你启动 Spring Boot 应用时,实际上是启动了一个内置的 Tomcat 实例来承载你的 Controller(本质上是 Servlet 的封装)。
5、总结
**Tomcat 是 Servlet 容器的最佳实践代表。** 它实现了 Java Servlet 规范,负责接收网络请求、解析协议、管理 Java 对象的生命周期,并将请求分发给具体的 Servlet 代码执行。理解 Tomcat 就是理解 Servlet 如何在服务器端被加载、管理和执行的过程。
四、Servlet容器与Engine、Host、Context和Wrapper
在 Tomcat 的架构中,Servlet 容器(Container) 并不是一个单一的对象,而是一个由 Engine、Host、Context 和 Wrapper 四个层级组成的嵌套树状结构。这种分层设计使得 Tomcat 能够灵活地管理从整个服务器引擎到单个 Servlet 实例的不同粒度资源。
具体关系如下图:

这四个组件形成了严格的父子关系:
Engine (父) → Host (子/父) → Context (子/父) → Wrapper (子)
当一个 HTTP 请求到达 Tomcat 时,容器的处理流程如下:
- Engine 层:接收请求,解析 HTTP Header 中的主机名,找到对应的 Host。
- Host 层:根据请求的 URL 路径(如
/myapp/api),识别出应用名称myapp,找到对应的 Context。 - Context 层:在当前的 Web 应用中,根据剩余的路径(如
/api),查找映射表,找到处理该路径的具体 Servlet,即对应的 Wrapper。 - Wrapper 层:加载或获取 Servlet 实例,执行其
service()方法,处理业务逻辑并生成响应。
这四个组件共同构成了 Tomcat 的核心处理管道,请求会依次经过这四个层级,最终到达具体的 Servlet 进行业务处理。
1、 四层容器架构详解
Engine(引擎):顶层容器
- 角色:代表整个 Catalina Servlet 引擎,是容器层级的最高级别。
- 功能:
- 接收来自所有连接器(Connector)的请求。
- 检查 HTTP 请求中的
Host头信息,决定将请求转发给哪个虚拟主机(Host)处理。 - 一个 Service 通常只包含一个 Engine。
- 对应配置:
<Engine name="Catalina" defaultHost="localhost">
Host(虚拟主机):第二层容器
- 角色:代表一个虚拟主机(Virtual Host),通常对应一个域名(如
www.example.com或localhost)。 - 功能:
- 管理该域名下部署的所有 Web 应用(Context)。
- 通过
appBase属性定义应用程序存放的物理目录(默认为webapps)。 - 一个 Engine 可以包含多个 Host,实现单台服务器托管多个域名的网站。
- 对应配置:
<Host name="localhost" appBase="webapps" ...>
Context(上下文):第三层容器
- 角色:代表一个具体的 Web 应用程序(即一个
.war包或一个项目目录)。 - 功能:
- 提供 Web 应用所需的运行环境,包括 JNDI 资源查找、会话(Session)管理、类加载隔离等。
- 解析
WEB-INF/web.xml或注解,建立 URL 路径与 Servlet 的映射关系。 - 一个 Host 可以包含多个 Context,对应部署在该域名下的不同应用(如
/app1,/app2)。
- 对应配置:
<Context path="/docs" docBase="docs" ...>
Wrapper(包装器):底层容器
- 角色:代表一个具体的 Servlet 实例,是容器的最小单元。
- 功能:
- 负责管理单个 Servlet 的生命周期:加载类、创建实例、调用
init()、执行service()、调用destroy()。 - 处理单线程模型(如果适用)和多线程并发访问。
- 一个 Context 可以包含多个 Wrapper,对应应用中的不同 Servlet。
- 负责管理单个 Servlet 的生命周期:加载类、创建实例、调用
- 对应配置:通常由 Tomcat 自动根据
web.xml或注解生成,无需手动配置<Wrapper>标签。
2、如何完成四步走
-
根据协议和端口号选定
Service和Engine。我们知道
Tomcat的每个连接器都监听不同的端口,比如Tomcat默认的HTTP连接器监听8080端口、默认的AJP连接器监听8009端口。上面例子中的URL访问的是8080端口,因此这个请求会被HTTP连接器接收,而一个连接器是属于一个Service组件的,这样Service组件就确定了。我们还知道一个Service组件里除了有多个连接器,还有一个容器组件,具体来说就是一个Engine容器,因此Service确定了也就意味着Engine也确定了。 -
根据域名选定
Host。Service和Engine确定后,Mapper组件通过url中的域名去查找相应的Host容器,比如例子中的url访问的域名是manage.xxx.com,因此Mapper会找到Host1这个容器。 -
根据
url路径找到Context组件。Host确定以后,Mapper根据url的路径来匹配相应的Web应用的路径,比如例子中访问的是/user,因此找到了Context1这个Context容器。 -
根据
url路径找到Wrapper(Servlet)。Context确定后,Mapper再根据web.xml中配置的Servlet映射路径来找到具体的Wrapper和Servlet,例如这里的Wrapper1的/list。
五、Tomcat与Spring关系
Servlet 技术是 Web 开发的原点,几乎所有的 Java Web 框架(比如 Spring)都是基于 Servlet 的封装,Spring 应用本身就是一个 Servlet(DispatchSevlet),而 Tomcat 和 Jetty 这样的 Web 容器,负责加载和运行 Servlet。
Tomcat 也能处理请求,为什么会用到Spring系列框架?
拿SpringMVC框架举例,Tomcat 开发效率、代码结构、功能扩展性、企业级特性,都不如SpringMVC。Tomcat 是运行环境,负责底层网络通信和 Servlet 规范实现;Spring MVC 是开发框架,负责上层业务逻辑组织。两者结合可大幅提升 Web 应用的开发效率和可维护性。

1、Tomcat
Tomcat 启动流程:startup.sh -> catalina.sh start ->java -jar org.apache.catalina.startup.Bootstrap.main()
1.1 Tomcat 核心功能
Tomcat 实现的 2 个核心功能:
- 处理
Socket连接,负责网络字节流与Request和Response对象的转化。 - 加载并管理
Servlet,以及处理具体的Request请求。
所以 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)。连接器负责对外交流,容器负责内部处理
Tomcat为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。

- Server 对应的就是一个 Tomcat 实例。
- Service 默认只有一个,也就是一个 Tomcat 实例默认一个 Service。
- Connector:一个 Service 可能多个 连接器,接受不同连接协议。
- Container: 多个连接器对应一个容器,顶层容器其实就是 Engine。
每个组件都有对应的生命周期,需要启动,同时还要启动自己内部的子组件,比如一个 Tomcat 实例包含一个 Service,一个 Service 包含多个连接器和一个容器。而一个容器包含多个 Host, Host 内部可能有多个 Context 容器,而一个 Context 也会包含多个 Servlet,所以 Tomcat 利用组合模式管理组件每个组件,对待多个也像对待单个组一样对待。整体上每个组件设计就像是「俄罗斯套娃」一样。
1.2 Tomcat 连接器组件
Tomcat支持多种 I/O 模型和应用层协议。
Tomcat支持的 I/O 模型有:
NIO:非阻塞I/O,采用Java NIO类库实现。NIO2:异步I/O,采用JDK 7最新的NIO2类库实现。APR:采用Apache可移植运行库实现,是C/C++编写的本地库。
Tomcat 支持的应用层协议有:
HTTP/1.1:这是大部分 Web 应用采用的访问协议。AJP:用于和 Web 服务器集成(如 Apache)。HTTP/2:HTTP 2.0 大幅度的提升了 Web 性能。
所以一个容器可能对接多个连接器。连接器对 Servlet 容器屏蔽了网络协议与 I/O 模型的区别,无论是 Http 还是 AJP,在容器中获取到的都是一个标准的 ServletRequest 对象。
细化连接器的功能需求:
- 监听网络端口。
- 接受网络连接请求。
- 读取请求网络字节流。
- 根据具体应用层协议(
HTTP/AJP)解析字节流,生成统一的Tomcat Request对象。 - 将
Tomcat Request对象转成标准的ServletRequest。 - 调用
Servlet容器,得到ServletResponse。 - 将
ServletResponse转成Tomcat Response对象。 - 将
Tomcat Response转成网络字节流。 - 将响应字节流写回给浏览器。
因此 Tomcat 的设计者设计了 3 个连接器组件来实现这 3 个功能,分别是 EndPoint、Processor 和 Adapter,连接器的三个核心组件分别做三件事情,其中 Endpoint和 Processor放在一起抽象成了 ProtocolHandler组件,它们的关系如下图所示。

根据实现的不同,ProtocolHandler又有如下分类:

1.2.1 ProtocolHandler 组件
主要处理 网络连接 和 应用层协议 ,包含了两个重要部件 EndPoint 和 Processor,两个组件组合形成 ProtocoHandler,下面我来详细介绍它们的工作原理。
EndPoint
EndPoint是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此 EndPoint是用来实现 TCP/IP 协议数据读写的,本质调用操作系统的 socket 接口。
EndPoint是一个接口,对应的抽象实现类是 AbstractEndpoint,而 AbstractEndpoint的具体子类,比如在 NioEndpoint和 Nio2Endpoint中,有两个重要的子组件:Acceptor和 SocketProcessor。
其中 Acceptor 用于监听 Socket 连接请求。SocketProcessor用于处理 Acceptor 接收到的 Socket请求,它实现 Runnable接口,在 Run方法里调用应用层协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor被提交到线程池来执行。
我们知道,对于 Java 的多路复用器的使用,无非是两步:
- 创建一个 Seletor,在它身上注册各种感兴趣的事件,然后调用 select 方法,等待感兴趣的事情发生。
- 感兴趣的事情发生了,比如可以读了,这时便创建一个新的线程从 Channel 中读数据。
在 Tomcat 中 NioEndpoint 则是 AbstractEndpoint 的具体实现,里面组件虽然很多,但是处理逻辑还是前面两步。它一共包含 LimitLatch、Acceptor、Poller、SocketProcessor和 Executor 共 5 个组件,分别分工合作实现整个 TCP/IP 协议的处理。
-
LimitLatch是连接控制器,它负责控制最大连接数,NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒绝。 -
Acceptor跑在一个单独的线程里,它在一个死循环里调用accept方法来接收新连接,一旦有新的连接请求到来,accept方法返回一个Channel对象,接着把Channel对象交给 Poller 去处理。 -
Poller的本质是一个Selector,也跑在单独线程里。Poller在内部维护一个Channel数组,它在一个死循环里不断检测Channel的数据就绪状态,一旦有Channel可读,就生成一个SocketProcessor任务对象扔给Executor去处理。 -
SocketProcessor实现了Runnable接口,其中run方法中的getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);代码则是获取handler并执行处理socketWrapper,最后通过socket 获取合适应用层协议处理器,也就是调用Http11Processor组件来处理请求。Http11Processor读取Channel的数据来生成ServletRequest对象,Http11Processor并不是直接读取 Channel 的。这是因为 Tomcat 支持同步非阻塞 I/O 模型和异步 I/O 模型,在 Java API 中,相应的 Channel 类也是不一样的,比如有AsynchronousSocketChannel和SocketChannel,为了对Http11Processor屏蔽这些差异,Tomcat 设计了一个包装类叫作SocketWrapper,Http11Processor只调用SocketWrapper的方法去读写数据。 -
Executor就是线程池,负责运行SocketProcessor任务类,SocketProcessor的run方***调用Http11Processor来读取和解析请求数据。我们知道,Http11Processor是应用层协议的封装,它会调用容器获得响应,再把响应通过Channel写出。
工作流程如下所示:

Processor
Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint的 Socket,读取字节流解析成 Tomcat Request和 Response对象,并通过 Adapter将其提交到容器处理,Processor是对应用层协议的抽象。

从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方调用 HttpProcessor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法,方法内部通过 以下代码将请求传递到容器中。
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
1.2.2 Adapter 组件
由于协议的不同,Tomcat 定义了自己的 Request 类来存放请求信息,这里其实体现了面向对象的思维。但是这个 Request 不是标准的 ServletRequest ,所以不能直接使用 Tomcat 定义 Request 作为参数直接容器。
Tomcat 设计者的解决方案是引入 CoyoteAdapter,这是适配器模式的经典运用,Acceptor监听连接生成 SocketProcessor 调用 CoyoteAdapter 的 Sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter负责将 Tomcat Request 转成 ServletRequest,再调用容器的 Service方法。
2、Spring
Spring 是一个轻量级的开源框架,旨在简化 Java 开发。
- 核心模块:
- Spring Core / IoC 容器:管理对象的创建和依赖关系(依赖注入 DI)。
- Spring AOP:面向切面编程,提供事务、日志等横切关注点的支持。
- Spring MVC:基于 Servlet 的 Web 框架,用于构建灵活、模块化的 Web 应用。
- Spring Boot:简化 Spring 配置,可内嵌 Tomcat 等服务器,实现“一键运行”。
- 作用:提供一套完整的编程模型和基础设施,让开发者更关注业务逻辑而非底层细节。
3、它们之间的关系
互补而非竞争:Tomcat 是 运行时环境(容器),Spring 是 应用框架(代码组织方式)。
典型协作模式:
- Spring MVC 的
DispatcherServlet本身就是一个 Servlet,它需要被注册并运行在 Tomcat 这样的 Servlet 容器中。 - Tomcat 负责处理底层网络通信(如监听端口、解析 HTTP 协议),将请求转交给 Spring MVC 的
DispatcherServlet;DispatcherServlet再根据 Spring 的配置(如@Controller方法)进行业务处理。 - Spring Boot 内嵌 Tomcat:Spring Boot 可以将 Tomcat 作为依赖内嵌到应用中,无需单独安装 Tomcat,直接运行
java -jar即可启动一个带服务器的 Spring 应用。
总结一句话:Tomcat 是房子(提供水电基础环境),Spring 是家具和装修(让居住更舒适、功能更丰富)。 一个 Spring Web 应用可以放在 Tomcat 中运行,也可以放在其他 Servlet 容器(如 Jetty、Undertow)中。
六、 部署Tomcat
1、下载Tomcat
进入官网Http://tomcat.apache.org/,选择download,下载所需要的Tomcat版本。本文以Tomcat 9 为例。

注意自己的电脑是64位系统还是32位系统。其它应该就不用多废话了,不然显得大家都很弱智。

此处选择 64-bit Windows zip 版本,已适配将要部署的电脑系统。下载后直接解压。

注意解压缩后的文件夹路径不能有中文和特殊字符,至于为什么我认为不明白的人可以百度一下。此处不再提了
2、Tomcat环境变量配置
右击“我的电脑” → 属性 → 系统信息 → 高级系统设置 → 环境变量

点击 系统变量 选择 新建 填写以下的键值对
CATALINA_HOME=[解压缩后的文件夹绝对路径]

填写好以后,直接确定。
选择 path → 编辑 → 新建 。填入以下值
%CATALINA_HOME%\lib
%CATALINA_HOME%\lib\servlet-api.jar
%CATALINA_HOME%\lib\jsp-api.jar

为什么单独显示个 %JAVA_HOME%\bin 因为本人在测试的时候,提示需要JDK的 JAVA_HOME 作为环境变量才能正常启动 Tomcat 。
如果以前配置JDK的时候就有,那可以忽略。如果没有就新建个JAVA_HOME系统变量,把JDK在path中的环境变量改为 %JAVA_HOME%\bin 就行了。
Tomcat环境变量配置配置完成
3、配置Tomcat用户管理
进入tomcat9的目录的conf,我这里的路径是 ./apache-tomcat-9.0.117-windows-x64/apache-tomcat-9.0.117/conf ,选择“tomcat-users.xml”文件。

打开文件后在最后 一行代码的前面添加如下代码:
<!-- 此处设置为弱密码,可根据情况自定义用户名和密码 -->
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="admin" password="admin" roles="admin-gui"/>
<user username="tomcat" password="admin" roles="manager-gui"/>
在 Tomcat 中注册两个特定的权限角色。Tomcat 采用基于角色的访问控制(RBAC),必须先声明角色,才能将其分配给用户。
- **
manager-gui:允许用户访问 Manager App** 的 HTML 界面(路径通常为/manager/html)。通过此界面,用户可以部署、卸载、启动、停止 Web 应用程序,以及查看会话状态等。 - **
admin-gui:允许用户访问 Host Manager** 的 HTML 界面(路径通常为/host-manager/html)。通过此界面,用户可以添加、删除或管理虚拟主机(Virtual Hosts)。

添加完成后保存并退出,完成用户配置。
4、tomcat测试
打开tomcat目录下的bin 文件夹里有启动和关闭的文件

startup 来启动 tomcat ;shutdown 来关闭 tomcat 。后缀是 .sh 用于Linux系统 ; 后缀 .bat 用于Windows系统。
在 bin 文件夹 下打开终端窗口,输入 startup.bat

此时会打开一个 tomcat 在线终端,会随时显示进出栈情况。适用浏览器登录 http://localhost:8080/

此时代表部署Tomcat 成功!
5、遇到的问题
有时会有驱动不了的情况,即tomcat 在线终端打开以后很快关闭。先这么处理:
-
通过
[你的Tomcat目录]/logs/catalina.[日期].log查看日志,会有报错信息java.net.BindException: Address already in use: bind可以先通过以下命令查看默认端口是否被占用netstat -ano | findstr :8080 netstat -ano | findstr :8005如果被占用,就到
[你的Tomcat目录]/conf/server.xml修改默认端口,或结束响应的进程,然后再启动Tomcat -
如果找不到端口被占用的信息,可以先重启电脑,然后再启动
Tomcat,以上两种情况排查后八成的问题就解决了。 -
这里处理另外两成的问题。使用以下命令:
netsh int ipv4 show dynamicport tcp查看系统保留端口范围。如果 8080 落在“起始端口”到“端口数”范围内,则说明该端口被系统保留。解决方法:更换端口(如 8081),或调整保留范围(不建议,较复杂)。
七、Servlet规范
以实践结合理论。
1、servlet组件(小型应用)–加载外部tomcat应用实例
1.1 编写一个简单的 servlet
编写一个标准的 Maven 项目对象模型(POM)配置文件,文件名为 pom.xml。它定义了一个名为 servletMemoryShell 的 Java 项目的基本信息、构建属性以及依赖库。代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>servletMemoryShell</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <!-- 关键:打包成 war -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope> <!-- 避免与 Tomcat 自带冲突 -->
</dependency>
</dependencies>
<build>
<finalName>servletMemoryShell</finalName> <!-- 生成 war 包的文件名,同时也是默认上下文路径 -->
</build>
</project>
编写一段测试代码 TestServlet.java 码如下:
package org.example;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().write("hello world");
}
}
1.2让它们动起来
现在有了两段代码,那么怎么能动起来呢?需要将 Servlet( 即 TestServlet.java) 与 Maven 项目对象模型(POM)配置文件(pom.xml)关联,然后放到 tomcat应用服务器 中。这样才能跑起来。
这里使用 IntelliJ IDEA 2026.1.1 编辑器,而且一定要开启适用,或者你有钱买了会员。
具体步骤如下:
1.2.1 新建 Maven Web 项目
打开 IntelliJ IDEA,点击 New Project。
- 左侧选择 Maven,右侧设置:
- Name:
servletMemoryShell(与 artifactId 一致) - Location(位置):选择你希望的保存路径
- JDK:选择 1.8 或更高版本
- Catalog(目录):Internal(内部)
- Archetype:选择
maven-archetype-quickstart
更简单的方法:直接选择 Maven 并勾选 Create from archetype,然后选择org.apache.maven.archetypes:maven-archetype-webapp。这个原型会生成标准的 web 项目结构(src/main/webapp/)。但为了让你清楚每一个配置文件的作用,以下步骤采用不勾选原型的方式(即普通 Maven 项目),然后手动补全必要目录。
- Name:
- 点击 Create(创建)。

1.2.2 调整项目结构并添加文件
在项目根目录下,手动创建以下目录(右键项目 → New (新建)→ Directory(文件夹))
src/main/java
src/main/resources
src/main/webapp/WEB-INF

替换创建的项目中的 pom.xml 文件内容,有些小修改
- 添加
<packaging>war</packaging>(告诉 Maven 构建 war 包)。 - 为 servlet-api 添加
<scope>provided</scope>,因为 Tomcat 已自带该库,避免冲突。
然后点击右侧的 同步 Maven 更改

会自动从网上加载相关配置,如果加载的有错误,就需要更换镜像源了。镜像源配置文件在 IntelliJ IDEA 2026.1.1 安装目录下的 ./plugins/maven/lib/maven3/conf/settings.xml 添加/修改 <mirrors> 配置,这里修改的是全局变量。
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

保存配置后,继续 同步 Maven 更改 。如果还是失败,那就是Maven 本地仓库的缓存文件导致解析失败,可以执行清理命令来强制更新。
-
在 IDEA 中清理:点击
File->Invalidate Caches...->Invalidate and Restart来清理 IDE 缓存并重启。 -
使用 Maven 命令:打开 IDEA 的
Terminal(终端)工具窗口,执行以下命令:# 清理本地仓库中下载失败的文件 mvn dependency:purge-local-repository # 解析并重新下载所有依赖和插件 mvn dependency:resolve # 重新构建项目 mvn clean install
1.2.3 让 IDEA 识别 Web 项目
确保 Maven 刷新:右键 pom.xml → Maven → Reload project(同步项目)。

由于我们手动创建了 webapp 目录,IDEA 可能尚未将其识别为 Web 资源根目录。需要配置 Facets:
- 按
Ctrl+Shift+Alt+S(或 File → Project Structure(项目结构))。 - 选择 Modules → 你的模块(servletMemoryShell)。
- 在中间窗口中点击 + → Web。
- 右侧的 Web Resource Directory 应指向
src/main/webapp(如果不对,点击铅笔修改)。 - 点击 OK。

1.2.4 配置 Tomcat 运行环境
在 IDEA 中配置:File → Settings → Build, Execution, Deployment → Application Servers → + → Tomcat Server → 选择 Tomcat 主目录 → OK。

现在,添加运行配置。就是添加 Tomcat应用服务器
点击右上角 Add Configuration…(或 Edit Configurations)。

点击 + → Tomcat Server → Local。

在 Server 标签页:
-
Name:
Tomcat 9 (或你选的版本) -
Application server:选择刚刚添加的 Tomcat。
-
JRE:选择项目使用的 JDK 1.8。
-
HTTP port:保持
8080(或其他可用端口)。
-
Deployment 标签页:
点击 + → Artifact → 选择servletMemoryShell:war exploded(推荐 exploded 模式,方便热部署)。- Application context:可设为
/或/servletMemoryShell。设为/则访问 URL 为http://localhost:8080/test;设为/servletMemoryShell则访问http://localhost:8080/servletMemoryShell/test。
- Application context:可设为
1.2.5 运行并测试
- 点击右上角绿色三角形(运行),或按
Shift+F10。 - IDEA 会构建项目,启动 Tomcat,并部署 war exploded。
- 控制台输出
Starting ProtocolHandler ...表示启动成功。
此时使用浏览器登录,进行测试

2、servlet初始化与装载流程–嵌入式tomcat应用实例
为减少干扰,这里不采用我们下载的tomcat来运行我们的项目,我们使用嵌入式tomcat也就是所谓的tomcat-embed-core。
此处的实例才第一次涉及到内存马,当然也是最基础的。只为了解工作原理
2.1 配置项目
重开一个 Maven Web 项目,Marven项目模板(Archetype)使用 org.apache.maven.archetypes:maven-archetype-quickstart。

生成的项目结构如下

文件代码如下:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>servletMemoryShell_embed</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 嵌入式 Tomcat 核心 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.83</version>
<scope>compile</scope>
</dependency>
<!-- 嵌入式 Tomcat JSP 解析支持(Servlet 不需要,但保留不影响) -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.83</version>
<scope>compile</scope>
</dependency>
<!-- 可选:JUnit 测试(quickstart 会自带,可保留) -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 可选:便于直接运行 main 类的插件 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>org.example.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
说明:
- 必须添加
tomcat-embed-core和tomcat-embed-jasper(后者虽非 Servlet 必需,但嵌入式 Tomcat 通常建议带上以避免 ClassNotFoundException)。- 添加
exec-maven-plugin只是为了方便运行,也可以直接通过 IDE 运行 Main 类。
Main.java:
package org.example;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import java.io.File;
public class Main {
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
// Tomcat 9 以上必须显式调用 getConnector() 来创建默认连接器
tomcat.getConnector();
// 添加 Web 应用上下文,根路径为 "",实际工作目录为当前项目目录
Context context = tomcat.addWebapp("", new File(".").getAbsolutePath());
// 注册 Servlet
Tomcat.addServlet(context, "helloServlet", new HelloServlet());
// 映射 URL 路径到 Servlet 名
context.addServletMappingDecoded("/hello", "helloServlet");
tomcat.start();
// 保持服务运行,等待请求
tomcat.getServer().await();
}
}
HelloServlet.java:
package org.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("Hello, World!");
out.println("</body></html>");
}
}
将原本由 quickstart 生成的 pom.xml 内容替换为以上同名文件的内容(同时保留必要的 Maven 基本配置),然后进行项目同步。
在 src/main/java/org/example/ 目录下创建 Main.java 、HelloServlet.java,分别复制以上代码。

在文件 Main.java 点击右键,选择 运行 'Main.main()'

使用浏览器登录 http://localhost:8080/hello

2.2 servlet 初始化流程分析
2.2.1 主程序入口 (Main.java)
- 启动点:
org.example.Main类的main()方法 - 依赖配置: 通过
pom.xml引入嵌入式 Tomcat 9.0.83 核心库
2.2.2 Tomcat 实例创建阶段
Tomcat tomcat = new Tomcat();
- 创建内嵌式 Tomcat 服务器实例
- 此时 Tomcat 处于未配置状态
2.2.3 连接器配置阶段
tomcat.getConnector();
- 显式创建默认 HTTP 连接器(监听端口默认为 8080)
- 注释说明:Tomcat 9+ 版本需要手动调用此方法
2.2.4 Web 应用上下文初始化
Context context = tomcat.addWebapp("", new File(".").getAbsolutePath());
- 创建根路径(“”)的 Web 应用上下文
- 工作目录设置为当前项目目录
- 返回 Context 对象用于后续 Servlet 注册
2.2.5 Servlet 注册与映射
Tomcat.addServlet(context, "helloServlet", new HelloServlet());
context.addServletMappingDecoded("/hello", "helloServlet");
- 将
HelloServlet实例注册到上下文中 - 建立 URL 路径
/hello到 Servlet 名称helloServlet的映射关系
2.2.6 服务启动阶段
tomcat.start();
- 启动整个 Tomcat 服务器
- 触发所有组件的生命周期初始化
2.2.7 请求等待阶段
tomcat.getServer().await();
- 阻塞主线程,使服务器持续运行并监听客户端请求
2.3 servlet 装载流程分析
2.3.1 Servlet 类装载
- JVM 加载
HelloServlet类到内存 - 该类继承自
HttpServlet,实现doGet()方法处理 GET 请求
2.3.2 Servlet 实例化
new HelloServlet()
- 在注册时直接创建 Servlet 实例(非懒加载模式)
- 调用无参构造函数完成对象初始化
2.3.3 Servlet 上下文绑定
- 通过
Tomcat.addServlet()将实例绑定到指定的 Web 上下文 - 分配唯一标识符
"helloServlet"
2.3.4 URL 映射建立
- 使用
addServletMappingDecoded()建立路由规则 /hello→helloServlet的映射关系存入容器内部数据结构
2.3.5 注解扫描(可选)
- 虽然存在
@WebServlet("/hello")注解,但由于采用了编程式注册方式,注解在此处不起作用 - 若使用自动扫描机制,则会通过注解发现 Servlet
2.3.6 生命周期管理准备
- Tomcat 容器准备好管理 Servlet 的生命周期(init/service/destroy)
- 当收到匹配
/hello的请求时,容器会调用对应的doGet()方法
2.3.7 响应处理机制
- 客户端访问
http://localhost:8080/hello时:- 容器定位到已注册的 HelloServlet 实例
- 调用其
doGet()方法生成 HTML 响应内容 - 返回 “Hello, World!” 文本给浏览器
2.4 嵌入式Tomcat Servlet项目总结
2.4.1 项目概述
这是一个基于嵌入式Apache Tomcat的Servlet内存Shell项目,采用编程式配置方式实现Web服务,无需传统WAR包部署。非常适合微服务和内存马场景。
2.4.2 技术架构
- 核心技术: Apache Tomcat 9.0.83 嵌入式版本
- 开发语言: Java 8/21
- 构建工具: Maven
- 关键依赖: tomcat-embed-core, tomcat-embed-jasper
2.4.3 初始化流程要点
- 实例创建 - 通过
new Tomcat()创建内嵌服务器 - 连接器配置 - 调用
getConnector()建立HTTP监听(端口8080) - 上下文构建 - 使用
addWebapp()创建根路径Web应用 - 组件注册 - 编程式注册Servlet并建立URL映射
- 服务启动 - 执行
start()完成生命周期初始化 - 请求监听 - 通过
await()保持服务运行状态
2.4.4 装载流程特点
- 即时实例化 - Servlet对象在注册时立即创建(非懒加载)
- 路由绑定 - URL路径与Servlet名称建立映射关系
- 注解兼容 - 支持@WebServlet注解但本项目采用显式注册
- 生命周期管理 - 容器统一管理init/service/destroy过程
- 动态响应 - 根据URL匹配调用对应处理方法
2.4.5 核心优势
- 零配置文件 - 所有配置通过代码完成
- 轻量级部署 - 无需外部Tomcat服务器
- 快速启动 - 嵌入式架构减少启动时间
- 灵活扩展 - 便于集成到微服务架构中
- 内存马特性 - 适合安全研究和动态注入场景
2.4.6 应用场景
- 微服务架构中的内嵌Web服务
- 安全测试中的内存Shell实现
- 快速原型开发的HTTP接口服务
- 需要动态部署Servlet的特殊场景
3、Filter容器与FilterDefs、FilterConfigs、FilterMaps、FilterChain
在 Java Servlet 规范(如 Tomcat 实现)中,Filter 机制用于对请求和响应进行预处理和后处理。下面逐一解释。
3.1 概念
在 Java Servlet 规范(如 Tomcat 实现)中,Filter 机制用于对请求和响应进行预处理和后处理。下面逐一解释 Filter 容器、FilterDefs、FilterConfigs、FilterMaps、FilterChain 是什么、各自有何特点与功能。
1. Filter 容器(Filter Container)
-
Filter 容器是 Servlet 容器(如 Tomcat)内部的一个子组件(严格说是Tomcat容器内的子容器),负责管理所有 Filter 的生命周期(加载、实例化、初始化、销毁)和执行顺序。
-
特点
- 内嵌于 Servlet 容器中,对开发者透明。
- 根据部署描述符(
web.xml)或注解(@WebFilter)动态注册 Filter。 - 为每个请求动态构建对应的 FilterChain。
-
功能
- 解析配置,生成
FilterDef、FilterMap等元数据对象。 - 实例化 Filter 并调用其
init(FilterConfig)方法。 - 请求到达时,匹配 URL 或 Servlet 名称,找到适用的 Filter 集合,按顺序生成
FilterChain。 - 容器关闭时,调用所有 Filter 的
destroy()方法。
- 解析配置,生成
2. FilterDef
-
FilterDef是 Tomcat 等容器内部使用的数据结构,用于存储 Filter 的静态定义信息(相当于配置的镜像)。它不属于 Servlet 规范中的公开接口,而是具体实现(如 Catalina)中的对象。 -
特点
- 包含:
filterName(唯一名称)、filterClass(全限定类名)、初始化参数(Map<String, String>)。 - 一个
FilterDef对应一个<filter>配置项或@WebFilter注解。 - 不包含过滤器的实例(实例由容器根据
FilterDef创建)。
- 包含:
-
功能
- 作为 Filter 的“配方”,供容器在需要时实例化 Filter。
- 支持动态添加(如通过
ServletContext.addFilter()返回的FilterRegistration.Dynamic底层会生成FilterDef)。 - 与
FilterConfig关联,为 Filter 提供初始化参数。
3. FilterConfig
-
javax.servlet.FilterConfig是 Servlet 规范中的标准接口,由容器实现。当 Filter 被初始化(init()方法)时,容器将FilterConfig对象传递给 Filter。 -
特点
- 每个 Filter 实例拥有自己唯一的
FilterConfig(在init方法中传入)。 - 它是 运行时只读 的配置视图。
- 提供的方法:
getFilterName()、getInitParameter(String name)、getInitParameterNames()、getServletContext()。
- 每个 Filter 实例拥有自己唯一的
-
功能
- 让 Filter 获取自身配置中的初始化参数(例如字符编码、日志级别等)。
- 允许 Filter 访问
ServletContext(进而获取全局资源、请求转发等)。 - 在
init阶段将参数注入 Filter,避免硬编码。
4. FilterMap
-
FilterMap是 容器内部的对象,用于存储 Filter 的映射规则:一个 Filter 应该拦截哪些请求(URL 模式)或哪些具体的 Servlet。 -
特点
- 包含:
filterName、urlPatterns(数组)、servletNames(数组)。 - 映射顺序决定了 Filter 的执行顺序(在
web.xml中声明顺序;注解方式通过类名排序)。 - 可同时配置 URL 模式和 Servlet 名称(只要匹配任意一种即触发)。
- 包含:
-
功能
- 将 Filter 绑定到具体的请求路径(
/api/*、*.jsp)或 Servlet 名称(myServlet)。 - 容器根据
FilterMap集合,为每个请求匹配出需要执行的 Filter 列表。 - 支持精确匹配、前缀匹配、后缀匹配等规则(与 Servlet 映射规则一致)。
- 将 Filter 绑定到具体的请求路径(
5. FilterChain
-
javax.servlet.FilterChain是 代表多个 Filter 组成的调用链 的规范接口。容器按顺序执行doFilter()方法,直到链尾的 Servlet 或资源。 -
特点
- 每个请求都有自己独立的
FilterChain实例(由容器实时构建)。 - 链中的顺序由
FilterMap的顺序决定(web.xml或@WebFilter的urlPatterns顺序和类加载顺序综合决定)。 FilterChain的核心方法是doFilter(ServletRequest, ServletResponse),调用它将控制权交给链中的下一个 Filter(或目标资源)。
- 每个请求都有自己独立的
-
功能
- 实现责任链模式,让 Filter 能够:
- 在请求到达 Servlet 之前执行预处理(如鉴权、日志、请求包装)。
- 调用
chain.doFilter()放行。 - 在 Servlet 处理完成后执行后处理(如响应压缩、头信息修改)。
- 允许 Filter 中断链(不调用
chain.doFilter()则直接返回,请求不再继续)。 - 管理异常传递,若某个 Filter 抛出未处理异常,链会中断。
- 实现责任链模式,让 Filter 能够:
6. 总结表格
| 概念 | 归属 | 主要角色 | 典型特点 |
|---|---|---|---|
| Filter 容器 | 容器内部 | 管理 Filter 总控 | 负责生命周期、链构建、请求分发 |
| FilterDef | 内部结构(如 Tomcat) | 存储静态定义 | 名称、类名、初始参数(不包含实例) |
| FilterConfig | 规范接口(javax.servlet) | 运行时配置视图 | 每个 Filter 一份,只读,init 时传入 |
| FilterMap | 内部结构 | 存储映射规则 | URL 模式 / Servlet 名称 + 顺序 |
| FilterChain | 规范接口 | 执行链 | 按顺序调用 doFilter,可中断 |
理解这五个概念,就能透彻掌握 Servlet Filter 的设计原理与实现机制。
3.2 它们之间的关系(协作流程)
-
部署/启动时
- 容器读取配置 → 为每个
<filter>或@WebFilter创建 FilterDef。 - 根据
<filter-mapping>或注解中的urlPatterns/servletNames创建 FilterMap。 - 通过 FilterConfig 持有 FilterDef 的初始化参数及 ServletContext,在
Filter.init(config)时传给 Filter。
- 容器读取配置 → 为每个
-
请求到达时
- 容器根据请求的 URI 匹配所有 FilterMap,得到有序的 Filter 名称列表。
- 容器检查这些名称对应的 Filter 实例是否已创建(未创建则依据 FilterDef 实例化并调用
init)。 - 容器将这些 Filter 实例构建成 FilterChain 对象。
- 调用
chain.doFilter()开始执行第一个 Filter。
-
Filter 内部
- Filter 可以从 FilterConfig 获得初始参数。
- 执行自己的逻辑后,调用
chain.doFilter()将请求传递给下一个 Filter,直到最终到达 Servlet。 - 若所有 Filter 都调用了
chain.doFilter(),则响应再反向经过各 Filter(后置处理)。
-
销毁时
- 容器调用每个 Filter 的
destroy()方法(FilterConfig 不再可用)。
- 容器调用每个 Filter 的
3.3 Filter实例
我们继续用之前在 七--1、servlet组件(小型应用)--加载外部tomcat应用实例 中搭建的环境,添加TestFilter.java 文件,代码(含注释)如下:
package org.example;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 自定义过滤器,通过 @WebFilter 注解将其映射到 "/test" 路径。
* 当客户端请求访问 "/test" 时,该过滤器的 doFilter 方法会被调用。
*/
@WebFilter("/test")
public class TestFilter implements Filter {
/**
* 初始化方法,在过滤器实例创建后、首次处理请求前由 Servlet 容器调用一次。
* 通常用于加载过滤器所需的配置参数或资源。
* @param filterConfig 提供过滤器的初始化参数和 ServletContext 访问
*/
public void init(FilterConfig filterConfig) {
// 输出 Filter初始化创建
System.out.println("[*] Filter initialization creation");
}
/**
* 核心过滤方法,每次请求匹配到该过滤器的 URL 模式时被调用。
* 可以执行请求预处理、调用下一个过滤器或目标资源、执行响应后处理。
* @param servletRequest 请求对象(可向下转型为 HttpServletRequest 以获得 HTTP 特有信息)
* @param servletResponse 响应对象(可向下转型为 HttpServletResponse)
* @param filterChain 过滤器链,调用其 doFilter 方法将请求传递给下一个过滤器或最终 Servlet
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
// 请求前置处理:打印日志,表示过滤器正在执行
System.out.println("[*] Filter performs filtering operations");
// 放行请求:将控制权交给过滤器链中的下一个过滤器或目标 Servlet
// 如果不调用此行,请求将在此中断,后续资源不会被访问
filterChain.doFilter(servletRequest, servletResponse);
// 请求后置处理:可以在 chain.doFilter() 之后添加响应增强逻辑(例如压缩、添加头信息等)
}
/**
* 销毁方法,当 Servlet 容器正常关闭或该过滤器被移除时调用一次。
* 用于释放过滤器所占用的资源(如数据库连接、文件流等)。
*/
public void destroy() {
System.out.println("[*] Filter destroyed");
}
}
跑起来之后,控制台输出[*] Filter initialization creation([*] Filter初始化创建),当我们访问/test路由的时候,控制台继续输出[*] Filter performs filtering operations([*] Filter执行过滤操作),当我们结束tomcat的时候,会触发destroy方法,从而输出[*] Filter destroyed([*] Filter已销毁):

3.4 实例中中Filter的运行流程
我来帮你分析这个项目中Filter的运行流程。首先让我查看项目中的相关文件。
3.4.1 应用启动阶段
当Web应用部署到Servlet容器(如Tomcat)并启动时:
- 容器扫描注解:Servlet容器会扫描所有带有
@WebFilter注解的类 - 创建Filter实例:容器为
TestFilter创建一个实例 - 调用init方法:容器调用
TestFilter.init()方法进行初始化- 此时会在控制台输出:
[*] Filter initialization creation
- 此时会在控制台输出:
3.4.2 请求处理阶段
当客户端发送HTTP请求访问/test路径时:
-
URL匹配:容器检查请求URL
/test是否匹配任何已注册的Filter -
执行doFilter方法:由于
TestFilter被配置为拦截/test路径,容器调用其doFilter方法 -
前置处理:在
doFilter方法中首先执行:System.out.println("[*] Filter performs filtering operations"); -
继续过滤链:调用
filterChain.doFilter(servletRequest, servletResponse)将请求传递给下一个组件 -
目标Servlet执行:最终到达
TestServlet的doGet方法,返回"hello world"响应 -
后置处理:如果需要在响应返回前做额外处理,可以在
chain.doFilter()之后添加逻辑
3.4.3 应用关闭阶段
当Web应用停止或服务器关闭时:
- 调用destroy方法:容器调用
TestFilter.destroy()方法 - 资源清理:释放过滤器占用的资源
- 销毁实例:Filter实例被垃圾回收
3.4.4 关键特性说明
- 注解配置:使用
@WebFilter("/test")将Filter绑定到特定URL模式 - 过滤链机制:通过
FilterChain实现多个Filter的顺序执行 - 生命周期管理:由Servlet容器统一管理Filter的创建、初始化和销毁
- 请求拦截:可以在请求到达Servlet之前和响应返回客户端之前进行干预
4、Listener组件(监听器)
在 Java Servlet 规范中,监听器(Listener) 是 Web 应用中的一种核心组件,它用于监听 Servlet 容器(如 Tomcat)内部发生的各种事件(事件源包括 Servlet 上下文、会话 Session、请求对象等),当事件发生时,容器会回调监听器中对应的方法,从而让开发者能够在特定时机执行自定义的逻辑。
监听器本质上就是事件驱动模型在 Servlet 容器中的实现,类似于 JavaScript 中的 addEventListener 或 GUI 编程中的按钮点击监听。
4.1 监听器的主要作用
- 监听 Web 应用的启动与关闭 – 在应用启动时初始化数据库连接池、加载配置文件、启动后台线程;在应用关闭时释放资源。
- 监听会话(Session)的创建与销毁 – 统计在线人数、记录用户登录/登出日志、清理用户会话数据。
- 监听请求(Request)的创建与销毁 – 记录请求日志、设置请求级别的初始参数。
- 监听属性(Attribute)的变化 – 当在
ServletContext、HttpSession、ServletRequest中添加、删除或替换属性时,执行相应操作(例如数据变更审计)。
4.2 Servlet 监听器的分类
按照监听的事件对象,可以分为三大类,每个大类下又分为生命周期监听器和属性变化监听器。
4.2.1 监听 ServletContext(应用上下文)
| 接口 | 事件类型 | 触发时机 |
|---|---|---|
ServletContextListener |
生命周期 | 应用启动(contextInitialized)应用关闭( contextDestroyed) |
ServletContextAttributeListener |
属性变化 | 在 ServletContext 中添加、删除、替换属性时 |
4.2.2 监听 HttpSession(会话)
| 接口 | 事件类型 | 触发时机 |
|---|---|---|
HttpSessionListener |
生命周期 | 会话创建(sessionCreated)会话销毁( sessionDestroyed) |
HttpSessionAttributeListener |
属性变化 | 在 HttpSession 中添加、删除、替换属性时 |
HttpSessionBindingListener |
绑定/解绑 | 对象级别的监听:当某个实现了该接口的 JavaBean 被绑定到会话或从会话中解绑时,会回调自身的方法(唯一无需在 web.xml 中配置的监听器) |
HttpSessionActivationListener |
钝化/活化 | 会话从内存序列化到磁盘(钝化)或从磁盘重新加载到内存(活化)时,用于分布式环境或内存回收场景 |
4.2.3 监听 ServletRequest(请求)
| 接口 | 事件类型 | 触发时机 |
|---|---|---|
ServletRequestListener |
生命周期 | 请求开始(requestInitialized)请求结束( requestDestroyed) |
ServletRequestAttributeListener |
属性变化 | 在 ServletRequest 中添加、删除、替换属性时 |
4.3 监听器的使用方式
方式一:注解注册(Servlet 3.0+)
在监听器类上直接标注 @WebListener,容器启动时会自动扫描并注册。
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class AppStartupListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("应用启动了,执行初始化工作...");
// 初始化数据库连接池、加载配置等
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("应用关闭了,释放资源...");
// 关闭线程池、数据库连接等
}
}
方式二:web.xml 配置
<listener>
<listener-class>com.example.AppStartupListener</listener-class>
</listener>
4.4 监听器的执行顺序
- 不同监听器类型之间:没有严格的顺序,但通常
ServletContextListener最早执行(应用启动时),之后HttpSessionListener(当第一次访问创建会话时),最后ServletRequestListener(每次请求)。 - 相同类型多个监听器:执行顺序取决于注册顺序(
web.xml中定义的顺序,或注解扫描的顺序,但规范不保证顺序,建议避免依赖顺序)。
4.5 与其他组件(Filter、Servlet)的关系
- Servlet:处理具体的业务请求。
- Filter:对请求/响应进行拦截加工,可以决定是否放行。
- Listener:被动观察,不修改请求/响应数据,只在特定事件发生时执行代码。
简而言之:
- Servlet 干活
- Filter 包装/筛选
- Listener 旁听/记录/辅助
它们共同组成了 Servlet 容器的三大扩展点(Servlet、Filter、Listener)。
4.5 监听器实例(ServletRequestListener)
4.5.1 创建实例
我们继续用之前在 七--1、servlet组件(小型应用)--加载外部tomcat应用实例 中搭建的环境,替换掉之前的TestFilter.java,重新写一个TestListener.java:
package org.example;
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
@WebListener("/test")
// "/test" 可不写
public class TestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("[+] destroy TestListener");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("[+] initial TestListener");
}
}
运行效果:

4.5.2 Listener运行的整体流程
先看个流程图:
本项目中Listener 的运行流程是:
- 启动阶段:Tomcat 扫描 @WebListener → 创建实例 → 注册到容器
- 请求阶段:请求到达 → requestInitialized() → Filter 链 → Servlet → requestDestroyed()
- 关闭阶段:应用停止 → 销毁 Listener 实例
与其它组件的区别:
- Filter:可以拦截、修改请求和响应(主动控制)
- Listener:只能监听事件,不能干预流程(被动观察)
- Listener 更适合:日志记录、性能监控、资源清理等横切关注点。这就是为什么在内存马场景中,Filter 比 Listener 更常用——因为 Filter 可以直接控制响应内容!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)