Servlet 体系结构
文章目录
- Servlet 类图
- SpringBoot 测试案例
- HttpServlet 原理
- 伪代码理解原理
- 理解差异
- 为什么 `HttpServlet` 实现 `service()`
Servlet 类图
---
title: Servlet 类图
---classDiagramdirection LRclass Servlet {<<interface>>+init(conf)+service(req,res)+destroy()}class GenericServlet {+getServletConfig()+getServletContext()}Servlet <|-- GenericServletclass HttpServlet {+doGet()+doPost()+doPut()+doDelete()+doHead()+doOptions()}GenericServlet <|-- HttpServletclass LegacyServlet {+init()+doGet()+destroy()}HttpServlet <|-- LegacyServlet
- Servlet:Servlet 体系根接口
- GenericServlet:Servlet 抽象实现类
- HttpServlet:对 HTTP 协议封装的 Servlet 实现类
SpringBoot 测试案例
创建 HttpServlet 测试类
@WebServlet("/demo")
public class ServletDemoSecond extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("get...");resp.setContentType("text/plain");resp.getWriter().write("GET SUCCESS");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");}
}
Servlet 规范不允许同一容器内多个 Servlet 映射到完全相同的 URL 路径,这会导致容器启动时抛出
IllegalArgumentException
。因为之前就是将 HttpServlet 测试类和 Servlet 测试类都制定同一个 URL,且 Servlet 测试类还设置了
loadOnStartup
导致了怎么都测试不了 HttpServlet 测试类
可以启动 SpringBoot 项目调用 /demo
接口进行测试,也可以通过 SpringBootTest 进行测试
需要以下依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>3.5.0</version><scope>test</scope>
</dependency><dependency><groupId>io.rest-assured</groupId><artifactId>rest-assured</artifactId><version>5.4.0</version>
</dependency>
创建测试类进行测试
@SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServletDemoTest {@LocalServerPortprivate int port;@Testpublic void testDoGet() {RestAssured.given().port(port).when().get("/demo").then().statusCode(HttpStatus.OK.value());}
}
启动服务输出结果
get...
2025-06-04T21:42:40.711+08:00 INFO 7686 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2025-06-04T21:42:42.717+08:00 INFO 7686 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
Servlet 销毁
测试类输出结果
get...
HttpServlet 原理
伪代码理解原理
通过伪代码了解 Servlet 和 HttpServlet 的关系,通过创建 MyHttpServlet
类来理解
public class MyHttpServlet implements Servlet {@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {// 根据请求方式不同,调用不同的方法HttpServletRequest req = (HttpServletRequest) servletRequest;// 获取请求方式String method = req.getMethod();// 判断请求方式if ("GET".equals(method)) {// GET 请求方式的处理逻辑doGet(servletRequest, servletResponse);} else if ("POST".equals(method)) {// POST 请求方式的处理逻辑doPost(servletRequest, servletResponse);}}protected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {System.out.println("调用 MyHttpServlet 的 doPost 方法");}protected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {System.out.println("调用 MyHttpServlet 的 doGet 方法");}
}
将之前的测试案例改成继承 MyHttpServlet
,即 public class ServletDemoSecond extends MyHttpServlet {}
@WebServlet("/demo")
public class ServletDemoSecond extends MyHttpServlet {@Overrideprotected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {super.doPost(servletRequest, servletResponse);System.out.println("post...");}@Overrideprotected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {super.doGet(servletRequest, servletResponse);System.out.println("get...");}
}
启动 SpringBoot 服务进行测试,控制台输出结果
调用 MyHttpServlet 的 doGet 方法
get...
理解差异
层级 | 是否协议相关 | service() 谁来实现 | 小结 |
---|---|---|---|
Servlet 接口 | 无 | 由 实现类 决定 | 只定义生命周期方法 init / service / destroy |
GenericServlet 抽象类 | 协议无关 | 仍然抽象→ 子类必须实现 | 把大量通用功能(getServletConfig()、getServletContext()、log() …)封装好 |
HttpServlet 抽象类 | 与 HTTP 绑定 | 自己实现,再分发到 doGet / doPost 等 | 你只需覆写需要的 “动词方法” |
GenericServlet
设计之初就强调 “Generic”——它既可能处理 FTP,也可能处理自定义二进制协议。没有任何通用逻辑可以写进 service()
,所以只能交给子类。
模版方法模式 Template Method:抽象类负责实现可复用的公共骨架(初始化,配置,日志),把变化点留给子类,实现开闭原则
为什么 HttpServlet
实现 service()
- 根据
HttpServlet
是针对于 HTTP 协议的 Servlet,HttpServlet.service()
可以统一读取request.getMethod()
,再路由到对应的doXXX()
方法 - 降低重复代码,如果
service()
还是抽象,所有业务 Servlet 都得自己写上述 switch 逻辑 → 代码膨胀且易错 - 因为已锁定 HTTP 协议,可以把“按方法分派”这一层通用逻辑封装好,再让你通过 doGet、doPost 专注业务。如此既减样板代码,又符合模板方法与责任分离的设计哲学