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容器的核心作用

  1. 生命周期管理
    容器控制Servlet的整个生命周期:
    • ‌**init()**‌:在Servlet首次加载时调用,完成初始化
    • ‌**service()**‌:每次请求到来时,容器调用此方法处理请求
    • ‌**destroy()**‌:应用关闭时释放资源
  2. 请求分发与路由
    容器根据URL映射(<url-pattern>)找到对应的Servlet,并将请求转发给它处理。例如,访问/hello,容器会调用HelloServletservice()方法。
  3. 多线程支持
    每个请求由独立线程处理,容器自动管理线程池,实现高并发响应。
  4. 与Web服务器集成
    容器通常内嵌于Web服务器(如Tomcat同时是Web服务器和Servlet容器),直接监听端口(如8080),接收HTTP请求并处理动态内容。
  5. 支持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 的核心作用

  1. 执行 Servlet 与 JSP
    • 当你访问一个由 Java 开发的网站(如电商后台、管理系统),请求会发送到 Tomcat。
    • Tomcat 负责加载并执行对应的 ‌Servlet‌,将动态内容生成 HTML 页面返回给浏览器。
    • 对于 JSP(JavaServer Pages)文件,Tomcat 会先将其编译成 Servlet,再执行。
  2. 处理 HTTP 请求
    • Tomcat 内置了 HTTP 服务器功能,可以直接监听 8080 等端口,接收浏览器的请求。
    • 它能解析 HTTP 协议,封装请求为 HttpServletRequest 对象,并调用相应组件处理。
  3. 支持多种协议与 I/O 模型
    • 支持 HTTP/1.1、HTTP/2 和 AJP 协议,可用于与 Nginx/Apache 集成。
    • 提供 NIO、NIO.2 和 APR 等 I/O 模型,提升高并发下的性能表现。
  4. 类加载隔离
    • 每个部署的应用都有独立的类加载器(ClassLoader),避免不同项目之间的依赖冲突。
  5. 热部署支持
    • 在开发过程中,修改代码后可设置 reloadable="true" 实现自动重启,无需手动停止服务。
  6. ‌**嵌入式使用(如 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 的容器部分采用分层架构,从大到小依次为:

  1. ‌**Engine(引擎)**‌:最高级别的容器,代表整个 Tomcat 服务。
  2. ‌**Host(主机)**‌:代表一个虚拟主机(通常对应一个域名)。
  3. ‌**Context(上下文)**‌:代表一个具体的 Web 应用程序(即一个 WAR 包或一个项目目录)。
  4. ‌**Wrapper(包装器)**‌:代表一个具体的 Servlet 实例。它是容器的最小单元,负责管理单个 Servlet 的生命周期。
2.2 请求处理流程

当浏览器发起一个 HTTP 请求时,Tomcat 作为 Servlet 容器的处理步骤如下:

  1. ‌**接收请求(Connector)**‌:
    • 连接器监听端口(如 8080),接收 TCP 连接。
    • 解析 HTTP 协议,将字节流转换为 Tomcat 内部的 Request 对象。
  2. ‌**映射路由(Mapper)**‌:
    • 根据请求的 URL(如 /api/user),在容器层级结构中找到对应的 Context(哪个项目)和 Wrapper(哪个 Servlet)。
  3. ‌**调用 Servlet(Container)**‌:
    • 容器创建标准的 HttpServletRequestHttpServletResponse 对象。
    • 如果 Servlet 尚未初始化,容器会加载类、创建实例并调用 init() 方法。
    • 容器调用 Servlet 的 service() 方法,传入请求和响应对象。
  4. 执行业务逻辑‌:
    • Servlet 执行开发者编写的代码(如查询数据库、计算数据)。
    • Servlet 将结果写入 HttpServletResponse
  5. 返回响应‌:
    • 容器获取响应对象中的数据。
    • 连接器将其转换为 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 时,容器的处理流程如下:

  1. Engine 层‌:接收请求,解析 HTTP Header 中的主机名,找到对应的 ‌Host‌。
  2. Host 层‌:根据请求的 URL 路径(如 /myapp/api),识别出应用名称 myapp,找到对应的 ‌Context‌。
  3. Context 层‌:在当前的 Web 应用中,根据剩余的路径(如 /api),查找映射表,找到处理该路径的具体 Servlet,即对应的 ‌Wrapper‌。
  4. 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.comlocalhost)。
  • 功能‌:
    • 管理该域名下部署的所有 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。
  • 对应配置‌:通常由 Tomcat 自动根据 web.xml 或注解生成,无需手动配置 <Wrapper> 标签。

2、如何完成四步走

  1. 根据协议和端口号选定ServiceEngine

    我们知道Tomcat的每个连接器都监听不同的端口,比如Tomcat默认的HTTP连接器监听8080端口、默认的AJP连接器监听8009端口。上面例子中的URL访问的是8080端口,因此这个请求会被HTTP连接器接收,而一个连接器是属于一个Service组件的,这样Service组件就确定了。我们还知道一个Service组件里除了有多个连接器,还有一个容器组件,具体来说就是一个Engine容器,因此Service确定了也就意味着Engine也确定了。

  2. 根据域名选定Host

    ServiceEngine确定后,Mapper组件通过url中的域名去查找相应的Host容器,比如例子中的url访问的域名是manage.xxx.com,因此Mapper会找到Host1这个容器。

  3. 根据url路径找到Context组件。

    Host确定以后,Mapper根据url的路径来匹配相应的Web应用的路径,比如例子中访问的是/user,因此找到了Context1这个Context容器。

  4. 根据url路径找到WrapperServlet)。

    Context确定后,Mapper再根据web.xml中配置的Servlet映射路径来找到具体的WrapperServlet,例如这里的Wrapper1/list

五、Tomcat与Spring关系

Servlet 技术是 Web 开发的原点,几乎所有的 Java Web 框架(比如 Spring)都是基于 Servlet 的封装,Spring 应用本身就是一个 Servlet(DispatchSevlet),而 TomcatJetty 这样的 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 连接,负责网络字节流与 RequestResponse 对象的转化。
  • 加载并管理 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的具体子类,比如在 NioEndpointNio2Endpoint中,有两个重要的子组件:AcceptorSocketProcessor

其中 Acceptor 用于监听 Socket 连接请求。SocketProcessor用于处理 Acceptor 接收到的 Socket请求,它实现 Runnable接口,在 Run方法里调用应用层协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor被提交到线程池来执行。

我们知道,对于 Java 的多路复用器的使用,无非是两步:

  1. 创建一个 Seletor,在它身上注册各种感兴趣的事件,然后调用 select 方法,等待感兴趣的事情发生。
  2. 感兴趣的事情发生了,比如可以读了,这时便创建一个新的线程从 Channel 中读数据。

在 Tomcat 中 NioEndpoint 则是 AbstractEndpoint 的具体实现,里面组件虽然很多,但是处理逻辑还是前面两步。它一共包含 LimitLatchAcceptorPollerSocketProcessorExecutor 共 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 类也是不一样的,比如有 AsynchronousSocketChannelSocketChannel,为了对 Http11Processor屏蔽这些差异,Tomcat 设计了一个包装类叫作 SocketWrapperHttp11Processor只调用 SocketWrapper的方法去读写数据。

  • Executor就是线程池,负责运行 SocketProcessor任务类,SocketProcessorrun方***调用 Http11Processor 来读取和解析请求数据。我们知道,Http11Processor是应用层协议的封装,它会调用容器获得响应,再把响应通过 Channel写出。

工作流程如下所示:

在这里插入图片描述

Processor

Processor 用来实现 HTTP 协议,Processor 接收来自 EndPointSocket,读取字节流解析成 Tomcat RequestResponse对象,并通过 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 调用 CoyoteAdapterSevice 方法,传入的是 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 的 DispatcherServletDispatcherServlet 再根据 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 来启动 tomcatshutdown 来关闭 tomcat 。后缀是 .sh 用于Linux系统 ; 后缀 .bat 用于Windows系统。

在 bin 文件夹 下打开终端窗口,输入 startup.bat

在这里插入图片描述

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

在这里插入图片描述

此时代表部署Tomcat 成功!

5、遇到的问题

有时会有驱动不了的情况,即tomcat 在线终端打开以后很快关闭。先这么处理:

  1. 通过 [你的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

  2. 如果找不到端口被占用的信息,可以先重启电脑,然后再启动 Tomcat,以上两种情况排查后八成的问题就解决了。

  3. 这里处理另外两成的问题。使用以下命令:

    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

  1. 左侧选择 Maven,右侧设置:
    • NameservletMemoryShell(与 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 项目),然后手动补全必要目录。
  2. 点击 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.xmlMavenReload 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 ServerLocal

在这里插入图片描述

Server 标签页:

  • NameTomcat 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
      在这里插入图片描述
1.2.5 运行并测试
  1. 点击右上角绿色三角形(运行),或按 Shift+F10
  2. IDEA 会构建项目,启动 Tomcat,并部署 war exploded。
  3. 控制台输出 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-coretomcat-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.javaHelloServlet.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() 建立路由规则
  • /hellohelloServlet 的映射关系存入容器内部数据结构
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 初始化流程要点
  1. 实例创建 - 通过 new Tomcat() 创建内嵌服务器
  2. 连接器配置 - 调用 getConnector() 建立HTTP监听(端口8080)
  3. 上下文构建 - 使用 addWebapp() 创建根路径Web应用
  4. 组件注册 - 编程式注册Servlet并建立URL映射
  5. 服务启动 - 执行 start() 完成生命周期初始化
  6. 请求监听 - 通过 await() 保持服务运行状态
2.4.4 装载流程特点
  1. 即时实例化 - Servlet对象在注册时立即创建(非懒加载)
  2. 路由绑定 - URL路径与Servlet名称建立映射关系
  3. 注解兼容 - 支持@WebServlet注解但本项目采用显式注册
  4. 生命周期管理 - 容器统一管理init/service/destroy过程
  5. 动态响应 - 根据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 容器FilterDefsFilterConfigsFilterMapsFilterChain 是什么、各自有何特点与功能。

1. Filter 容器(Filter Container)
  • Filter 容器是 Servlet 容器(如 Tomcat)内部的一个子组件(严格说是Tomcat容器内的子容器),负责管理所有 Filter 的生命周期(加载、实例化、初始化、销毁)和执行顺序

  • 特点

    • 内嵌于 Servlet 容器中,对开发者透明。
    • 根据部署描述符(web.xml)或注解(@WebFilter)动态注册 Filter。
    • 为每个请求动态构建对应的 FilterChain
  • 功能

    • 解析配置,生成 FilterDefFilterMap 等元数据对象。
    • 实例化 Filter 并调用其 init(FilterConfig) 方法。
    • 请求到达时,匹配 URL 或 Servlet 名称,找到适用的 Filter 集合,按顺序生成 FilterChain
    • 容器关闭时,调用所有 Filter 的 destroy() 方法。
2. FilterDef
  • FilterDefTomcat 等容器内部使用的数据结构,用于存储 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.FilterConfigServlet 规范中的标准接口,由容器实现。当 Filter 被初始化(init() 方法)时,容器将 FilterConfig 对象传递给 Filter。

  • 特点

    • 每个 Filter 实例拥有自己唯一的 FilterConfig(在 init 方法中传入)。
    • 它是 运行时只读 的配置视图。
    • 提供的方法:getFilterName()getInitParameter(String name)getInitParameterNames()getServletContext()
  • 功能

    • 让 Filter 获取自身配置中的初始化参数(例如字符编码、日志级别等)。
    • 允许 Filter 访问 ServletContext(进而获取全局资源、请求转发等)。
    • init 阶段将参数注入 Filter,避免硬编码。
4. FilterMap
  • FilterMap容器内部的对象,用于存储 Filter 的映射规则:一个 Filter 应该拦截哪些请求(URL 模式)或哪些具体的 Servlet。

  • 特点

    • 包含:filterNameurlPatterns(数组)、servletNames(数组)。
    • 映射顺序决定了 Filter 的执行顺序(在 web.xml 中声明顺序;注解方式通过类名排序)。
    • 可同时配置 URL 模式和 Servlet 名称(只要匹配任意一种即触发)。
  • 功能

    • 将 Filter 绑定到具体的请求路径(/api/**.jsp)或 Servlet 名称(myServlet)。
    • 容器根据 FilterMap 集合,为每个请求匹配出需要执行的 Filter 列表。
    • 支持精确匹配、前缀匹配、后缀匹配等规则(与 Servlet 映射规则一致)。
5. FilterChain
  • javax.servlet.FilterChain代表多个 Filter 组成的调用链 的规范接口。容器按顺序执行 doFilter() 方法,直到链尾的 Servlet 或资源。

  • 特点

    • 每个请求都有自己独立的 FilterChain 实例(由容器实时构建)。
    • 链中的顺序由 FilterMap 的顺序决定(web.xml@WebFilterurlPatterns 顺序和类加载顺序综合决定)。
    • FilterChain 的核心方法是 doFilter(ServletRequest, ServletResponse),调用它将控制权交给链中的下一个 Filter(或目标资源)。
  • 功能

    • 实现责任链模式,让 Filter 能够:
      • 在请求到达 Servlet 之前执行预处理(如鉴权、日志、请求包装)。
      • 调用 chain.doFilter() 放行。
      • 在 Servlet 处理完成后执行后处理(如响应压缩、头信息修改)。
    • 允许 Filter 中断链(不调用 chain.doFilter() 则直接返回,请求不再继续)。
    • 管理异常传递,若某个 Filter 抛出未处理异常,链会中断。
6. 总结表格
概念 归属 主要角色 典型特点
Filter 容器 容器内部 管理 Filter 总控 负责生命周期、链构建、请求分发
FilterDef 内部结构(如 Tomcat) 存储静态定义 名称、类名、初始参数(不包含实例)
FilterConfig 规范接口(javax.servlet) 运行时配置视图 每个 Filter 一份,只读,init 时传入
FilterMap 内部结构 存储映射规则 URL 模式 / Servlet 名称 + 顺序
FilterChain 规范接口 执行链 按顺序调用 doFilter,可中断

理解这五个概念,就能透彻掌握 Servlet Filter 的设计原理与实现机制。

3.2 它们之间的关系(协作流程)
  1. 部署/启动时

    • 容器读取配置 → 为每个 <filter>@WebFilter 创建 FilterDef
    • 根据 <filter-mapping> 或注解中的 urlPatterns / servletNames 创建 FilterMap
    • 通过 FilterConfig 持有 FilterDef 的初始化参数及 ServletContext,在 Filter.init(config) 时传给 Filter。
  2. 请求到达时

    • 容器根据请求的 URI 匹配所有 FilterMap,得到有序的 Filter 名称列表。
    • 容器检查这些名称对应的 Filter 实例是否已创建(未创建则依据 FilterDef 实例化并调用 init)。
    • 容器将这些 Filter 实例构建成 FilterChain 对象。
    • 调用 chain.doFilter() 开始执行第一个 Filter。
  3. Filter 内部

    • Filter 可以从 FilterConfig 获得初始参数。
    • 执行自己的逻辑后,调用 chain.doFilter() 将请求传递给下一个 Filter,直到最终到达 Servlet。
    • 若所有 Filter 都调用了 chain.doFilter(),则响应再反向经过各 Filter(后置处理)。
  4. 销毁时

    • 容器调用每个 Filter 的 destroy() 方法(FilterConfig 不再可用)。
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)并启动时:

  1. 容器扫描注解:Servlet容器会扫描所有带有@WebFilter注解的类
  2. 创建Filter实例:容器为TestFilter创建一个实例
  3. 调用init方法:容器调用TestFilter.init()方法进行初始化
    • 此时会在控制台输出:[*] Filter initialization creation
3.4.2 请求处理阶段

当客户端发送HTTP请求访问/test路径时:

  1. URL匹配:容器检查请求URL /test 是否匹配任何已注册的Filter

  2. 执行doFilter方法:由于TestFilter被配置为拦截/test路径,容器调用其doFilter方法

  3. 前置处理:在doFilter方法中首先执行:

    System.out.println("[*] Filter performs filtering operations");
    
  4. 继续过滤链:调用filterChain.doFilter(servletRequest, servletResponse)将请求传递给下一个组件

  5. 目标Servlet执行:最终到达TestServletdoGet方法,返回"hello world"响应

  6. 后置处理:如果需要在响应返回前做额外处理,可以在chain.doFilter()之后添加逻辑

3.4.3 应用关闭阶段

当Web应用停止或服务器关闭时:

  1. 调用destroy方法:容器调用TestFilter.destroy()方法
  2. 资源清理:释放过滤器占用的资源
  3. 销毁实例: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)的变化 – 当在 ServletContextHttpSessionServletRequest 中添加、删除或替换属性时,执行相应操作(例如数据变更审计)。
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运行的整体流程

先看个流程图:

Web应用启动

Tomcat扫描@WebServlet/@WebFilter/@WebListener

创建Listener实例

调用listener.contextInitialized

Listener注册到StandardContext

客户端发起请求

Tomcat接收请求

创建ServletRequest对象

触发requestInitialized事件

TestListener.requestInitialized被调用

执行前置处理逻辑

进入Filter链

执行Servlet.service

生成响应

请求处理完成

触发requestDestroyed事件

TestListener.requestDestroyed被调用

执行后置清理逻辑

Web应用停止

调用listener.contextDestroyed

销毁Listener实例

本项目中Listener 的运行流程是:

  1. 启动阶段:Tomcat 扫描 @WebListener → 创建实例 → 注册到容器
  2. 请求阶段:请求到达 → requestInitialized() → Filter 链 → Servlet → requestDestroyed()
  3. 关闭阶段:应用停止 → 销毁 Listener 实例

与其它组件的区别:

  1. Filter:可以拦截、修改请求和响应(主动控制)
  2. Listener:只能监听事件,不能干预流程(被动观察)
  3. Listener 更适合:日志记录、性能监控、资源清理等横切关注点。这就是为什么在内存马场景中,Filter 比 Listener 更常用——因为 Filter 可以直接控制响应内容!
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐