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 专注业务。如此既减样板代码,又符合模板方法与责任分离的设计哲学
相关推荐
凌辰揽月3 分钟前
8分钟讲完 Tomcat架构及工作原理
java·架构·tomcat
笑醉踏歌行9 分钟前
idea应用代码配色网站
java·ide·intellij-idea
一入JAVA毁终身11 分钟前
处理Lombok的一个小BUG
java·开发语言·bug
gjh120822 分钟前
Easy-excel监听器中对批量上传的工单做错误收集
java·spring boot
红衣女妖仙26 分钟前
JXLS 库导出复杂 Excel
java·excel·jxls·java 导出 excel
绝无仅有31 分钟前
对接三方SDK开发过程中的问题排查与解决
后端·面试·架构
Hellyc33 分钟前
JAVA八股文:异常有哪些种类,可以举几个例子吗?Throwable类有哪些常见方法?
java·开发语言
西岭千秋雪_42 分钟前
Redis缓存架构实战
java·redis·笔记·学习·缓存·架构
五岳1 小时前
深入研究使用DozerMapper复制List<Ojbect>前后元素类型不一致的问题
java·爬坑
人生在勤,不索何获-白大侠1 小时前
day15——Java常用API(二):常见算法、正则表达式与异常处理详解
java·算法·正则表达式