Servlet 体系结构

文章目录

    • [Servlet 类图](#Servlet 类图)
    • [SpringBoot 测试案例](#SpringBoot 测试案例)
    • [HttpServlet 原理](#HttpServlet 原理)

Servlet 类图

--- title: Servlet 类图 --- classDiagram direction LR class Servlet { <> +init(conf) +service(req,res) +destroy() } class GenericServlet { +getServletConfig() +getServletContext() } Servlet <|-- GenericServlet class HttpServlet { +doGet() +doPost() +doPut() +doDelete() +doHead() +doOptions() } GenericServlet <|-- HttpServlet class LegacyServlet { +init() +doGet() +destroy() } HttpServlet <|-- LegacyServlet
  • Servlet:Servlet 体系根接口
  • GenericServlet:Servlet 抽象实现类
  • HttpServlet:对 HTTP 协议封装的 Servlet 实现类

SpringBoot 测试案例

创建 HttpServlet 测试类

java 复制代码
@WebServlet("/demo")
public class ServletDemoSecond extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get...");
        resp.setContentType("text/plain");
        resp.getWriter().write("GET SUCCESS");
    }

    @Override
    protected 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 进行测试

需要以下依赖

xml 复制代码
<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>

创建测试类进行测试

java 复制代码
@SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServletDemoTest {

    @LocalServerPort
    private int port;

    @Test
    public void testDoGet() {
        RestAssured.given().port(port).when().get("/demo").then().statusCode(HttpStatus.OK.value());
    }
}

启动服务输出结果

shell 复制代码
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 销毁

测试类输出结果

shell 复制代码
get...

HttpServlet 原理

伪代码理解原理

通过伪代码了解 Servlet 和 HttpServlet 的关系,通过创建 MyHttpServlet 类来理解

java 复制代码
public class MyHttpServlet implements Servlet {
    @Override
    public 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 {}

java 复制代码
@WebServlet("/demo")
public class ServletDemoSecond extends MyHttpServlet {
    @Override
    protected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {
        super.doPost(servletRequest, servletResponse);
        System.out.println("post...");
    }

    @Override
    protected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {
        super.doGet(servletRequest, servletResponse);
        System.out.println("get...");
    }
}

启动 SpringBoot 服务进行测试,控制台输出结果

shell 复制代码
调用 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 专注业务。如此既减样板代码,又符合模板方法与责任分离的设计哲学
相关推荐
云烟成雨TD21 分钟前
Spring AI 1.x 系列【28】基于内存和 MySQL 的多轮对话实现案例
java·人工智能·spring
Lyyaoo.23 分钟前
【JAVA基础面经】String、StringBuffer、StringBuilder
java·开发语言
TeamDev30 分钟前
JxBrowser 8.18.2 版本发布啦!
java·前端·跨平台·桌面应用·web ui·jxbrowser·浏览器控件
晴天sir33 分钟前
Redis 在业务中的几种典型用法
java·数据库·redis
WJX_KOI38 分钟前
MemOS —— 为大语言模型 (LLMs) 和智能体打造的记忆操作系统。
java·人工智能·语言模型
何陋轩41 分钟前
AI时代,程序员何去何从?别慌,看完这篇你就明白了
后端·面试
_日拱一卒43 分钟前
LeetCode:矩阵置零
java·数据结构·线性代数·算法·leetcode·职场和发展·矩阵
weixin_4080996744 分钟前
OCR 识别率提升实战:模糊 / 倾斜 / 反光图片全套优化方案(附 Python / Java / PHP 代码)
图像处理·人工智能·后端·python·ocr·api·抠图
weixin_408099671 小时前
【实战教程】懒人精灵如何实现 OCR 文字识别?接口调用完整指南(附可运行示例)
java·前端·人工智能·后端·ocr·api·懒人精灵
珍朱(珠)奶茶1 小时前
Spring Boot3整合Jxls工具包实现模版excel导出文件
spring boot·后端·excel