Servlet完全上手:核心语法与生命周期详解

Servlet完全上手指南:核心语法与生命周期详解

  • Servlet完全上手指南:核心语法与生命周期详解
    • 一、引言
    • 二、使用IDEA创建servlet
    • 三、项目结构解析
      • [1. 项目根目录文件](#1. 项目根目录文件)
      • [2. src 目录](#2. src 目录)
        • [(1)src/main 目录](#(1)src/main 目录)
        • [(2)src/test 目录](#(2)src/test 目录)
      • [3. target 目录](#3. target 目录)
      • [4. 其他目录](#4. 其他目录)
    • 三、Servlet与JSP协同运行的全链路流程
      • [1. Servlet+JSP协同流程](#1. Servlet+JSP协同流程)
      • [2. 客户端(浏览器)​](#2. 客户端(浏览器))
    • 三、javax到jakarta的不兼容变更
    • [四、Servlet 生命周期](#四、Servlet 生命周期)
      • [1. 什么是Servlet生命周期?](#1. 什么是Servlet生命周期?)
      • [2. Servlet生命周期方法](#2. Servlet生命周期方法)
      • [3. 流程图](#3. 流程图)
    • [五、Servlet API](#五、Servlet API)
      • [1. 接口定义的方法](#1. 接口定义的方法)
      • [2. Servlet API重要的接口与类​](#2. Servlet API重要的接口与类)
      • [3. Servlet接口使用演示(生命周期)](#3. Servlet接口使用演示(生命周期))
      • [4. 运行结果](#4. 运行结果)

Servlet完全上手指南:核心语法与生命周期详解


一、引言

你是否曾好奇,当你点击一个网页链接或提交一个表单时,背后是谁在接收请求、处理数据、并最终生成你看到的页面?在Java Web开发的世界里,扮演这个核心角色的,正是​​Servlet​​------这位久经沙场的"老将"至今仍是整个Java EE体系无可争议的基石。

尽管如今各种高级框架(如Spring MVC)大行其道,但它们绝大多数依然构建在Servlet规范之上。理解Servlet,就如同掌握了打开Java Web开发大门的钥匙;它不仅能帮助你轻松上手传统项目,更能让你深刻理解现代框架的工作原理,做到知其然,亦知其所以然。

  • ​​一个Servlet的"样貌"是怎样的?​​ ------ 认识其核心语法与代码结构。

  • ​​它是如何"出生"、"工作"乃至"退休"的?​​ ------ 深入理解其至关重要的​​生命周期​​。

无论你是刚刚踏入Web开发的新手,还是希望巩固基础、厘清概念的经验开发者,这篇文章都将为你提供一个清晰而坚实的起点。让我们开始这段探索之旅,亲手揭开Servlet的神秘面纱。


二、使用IDEA创建servlet

首先在新建项目里面选择 Jakarta EE ,模板选择 Web 应用程序,服务器选择tomcat,语言为Java,构造系统为Maven,接着点下一步

一般IDEA会自动给你选好Servlet,我们这里可以选择把Hibernate Validator勾上

接下来点击创建,如果JSP程序可以运行,那就说明配置好了


三、项目结构解析

这是一个基于 Maven 构建的 Java Web 项目,主要用于学习或演示 Servlet 的生命周期相关内容。下面对项目结构进行详细解析:

1. 项目根目录文件

  • .gitignore:Git 版本控制的忽略文件,指定 Git 提交时应忽略的文件/目录,避免不必要文件纳入版本管理。
  • mvnwmvnw.cmd:Maven Wrapper 脚本文件,可在无全局 Maven 时,自动下载指定版本 Maven 构建项目,保证构建环境一致。
  • pom.xml:Maven 项目核心配置文件,配置项目坐标、依赖、构建插件等,Maven 据此管理项目构建与依赖。

2. src 目录

src 目录存放项目源代码和资源文件,分 maintest 子目录。

(1)src/main 目录

存放项目主要源代码和资源文件。

  • java 目录 :存放 Java 源代码。
    • com.demo1.lifecycle :Java 包结构,组织 Java 类,避免类名冲突。
      • HelloServlet :Servlet 类,用于演示 Servlet 生命周期(初始化、服务请求、销毁等阶段),继承 HttpServlet 并重写相关方法(如 init()doGet()doPost()destroy() 等)。
  • resources 目录:存放项目资源文件,如配置文件、国际化资源文件等(若有相关资源会放于此)。
  • webapp 目录 :Java Web 项目的 Web 应用根目录,部署到 Web 服务器(如 Tomcat)后,目录下文件可直接访问。
    • WEB-INF 目录 :Web 应用受保护目录,客户端无法直接访问,仅服务器端可访问。
      • web.xml :Web 应用部署描述符,配置 Servlet(将 HelloServlet 映射到特定 URL 路径)、过滤器、监听器、欢迎页面、错误页面等 Web 应用相关配置。
      • index.jsp :JSP(Java Server Pages)页面,动态网页技术,可在 HTML 中嵌入 Java 代码,可能作为欢迎页面或与 HelloServlet 交互的页面。
(2)src/test 目录

存放项目测试代码(如单元测试代码),保证代码正确性。

3. target 目录

Maven 构建项目时的输出目录,存放编译后的字节码文件、打包生成的 WAR 包、构建过程临时文件等。执行 Maven 的 compilepackage 等命令时,输出产物存于此目录。

4. 其他目录

  • .idea 目录:IntelliJ IDEA 集成开发环境的配置目录,包含项目设置、缓存、索引等信息,用于 IDEA 对项目的管理和开发支持。
  • .mvn 目录 :与 Maven Wrapper 相关,包含 Maven Wrapper 的配置文件,支持 mvnwmvnw.cmd 脚本运行。

综上,该项目结构是典型的 Maven 管理的 Java Web 项目结构,通过合理的目录划分和配置文件,方便进行 Servlet 相关功能的开发、构建和部署。


三、Servlet与JSP协同运行的全链路流程

1. Servlet+JSP协同流程

流程步骤 主体 核心动作 关键说明
1 浏览器 发送请求 向服务器发起包含目标资源(如HTML文档、userlogin.jsp等)的HTTP请求
2 服务器 判断请求类型 区分请求是静态(如HTML文档)还是动态(如JSP页面)
3 服务器(静态请求时) 返回HTML文档 直接将对应的HTML文档作为响应回传给浏览器
4 Web容器(动态请求时,如Tomcat) 创建Servlet对象 针对动态请求(如JSP),由Web容器实例化对应的Servlet类
5 Web容器 分配线程,调用方法 为Servlet处理请求分配线程,调用doGetdoPost方法执行业务逻辑
6 Servlet 生成响应 处理请求后,生成包含结果的HTTP响应
7 服务器 返回响应 将Servlet生成的响应回传给浏览器
8 浏览器 解析并展示响应 解析响应内容(如HTML、动态生成的页面等),呈现给用户

2. 客户端(浏览器)​

  1. 发送 HTTP 请求(目标为 ServletJSP,如/LoginServlet/index.jsp
    Web 服务器(如 Tomcat
  2. 监听端口接收请求,判断请求目标类型
    ├→ 若目标是 "普通 Servlet " → 【转发至 Servlet 容器】 → Servlet 容器(按映射规则定位对应的 Servlet 类)
    └→ 若目标是 "JSP " → 【触发内置 JSP 引擎(如 Tomcat 的 Jasper)】 → JSP 引擎(首次请求 / JSP 文件修改时,将 JSP 编译为 Java 源文件→再编译为字节码文件,该字节码本质是 Servlet 类) → 【编译完成后,将 Servlet 类交给 Servlet 容器】 → Servlet 容器(复用编译后的 JSP Servlet 类)
  3. Servlet 容器对 Servlet 进行初始化 (仅首次请求时执行:创建 Servlet 实例,调用init()方法加载配置 / 资源;非首次请求直接复用已有实例
    Servlet 实例(普通 Servlet 实例 或 编译后的 JSP Servlet 实例)
  4. 执行核心处理逻辑
    ├→ 若为 "普通 Servlet":调用service()方法,按请求类型分发至doGet()/doPost(),执行业务逻辑(如登录验证、数据查询)
    └→ 若为 "JSP Servlet":准备页面渲染相关逻辑
  5. 判断是否需要普通 Servlet 向 JSP 传递数据
    ├→ 是(如登录成功后需展示用户信息):普通 Servlet 通过request.setAttribute()存储数据 → 调用request.getRequestDispatcher()转发至目标 JSP → 【JSP 对应的 Servlet 实例】
    └→ 否(如直接访问 JSP 页面):直接进入【JSP 对应的 Servlet 实例】
  6. JSP 对应的 Servlet 实例执行渲染:通过 EL 表达式(${})/JSTL 标签从 Request 中获取数据,拼接动态 HTML 内容
  7. 生成响应:普通 Servlet 或 JSP Servlet 通过ServletResponse封装 HTTP 响应报文(含响应行、响应头、响应体)
  8. Web 服务器接收响应报文,通过网络回传至客户端(浏览器)
  9. 浏览器解析响应中的 HTML 内容(含动态数据),渲染并展示最终页面

三、javax到jakarta的不兼容变更

从 Tomcat 10 开始,Servlet API 的包命名空间发生了重大且不兼容的变更。​​

具体体现在:

  1. ​​命名空间改变​​:由旧的 javax.servlet.*全部改为 ​​jakarta.servlet.*​​。

  2. ​​规范变更​​:此变更是为了支持 ​​Jakarta EE 9​​ 及更高版本规范(如提及的 Servlet 6.0)。

  3. ​​直接后果​​:这个变化导致了​​向下不兼容​​。所有使用旧版 javax包开发的 Servlet 应用程序​​无法直接部署和运行在 Tomcat 10 及以后的版本中​​。

简单来说​​:如果您要将一个老项目升级到 Tomcat 10+,通常需要修改代码中的所有相关导入(import)语句和配置文件中的命名空间声明,将 javax.servlet替换为 jakarta.servlet。这是一个升级过程中必须注意的关键迁移步骤。


四、Servlet 生命周期

1. 什么是Servlet生命周期?

Servlet生命周期指的是一个Servlet实例从被创建到最终被垃圾回收的整个过程。这个过程完全由Servlet容器(如Tomcat、Jetty等)管理,包括以下几个关键阶段:

加载类 → 实例化 → init() → service() → doGet()/doPost() → destroy() → 垃圾回收

  1. Servlet 初始化后调用 init () 方法。
  2. Servlet 调用 service() 方法来处理客户端的请求。
  3. Servlet 销毁前调用 destroy() 方法。
  4. 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

2. Servlet生命周期方法

1. init () 方法

java 复制代码
public void init() throws ServletException {
  // 初始化代码...
}
  • 功能说明:
    init() 是 Servlet 的初始化方法,在 Servlet 实例被创建后仅执行一次。

  • 主要用途:

    执行一次性初始化操作,如加载配置文件、建立数据库连接池、初始化缓存等

    可以通过重写此方法完成 Servlet 启动时的准备工作

  • 特点:

    若初始化失败,会抛出 ServletException,导致 Servlet 无法正常提供服务

    可通过 web.xml 中的 配置,使 Servlet 在 Web 应用启动时就完成初始化

2. service () 方法

java 复制代码
public void service(ServletRequest request,ServletResponse response) 
      throws ServletException, IOException{
}
  • 功能说明:
    service() 是处理客户端请求的核心方法,每次客户端发送请求时都会被调用。

  • 主要用途:

    接收客户端请求(封装在 ServletRequest 对象中)

    处理请求并生成响应(通过 ServletResponse 对象返回)

  • 特点:

    会自动根据 HTTP 请求方法(GET、POST、PUT 等)调用对应的处理方法(如 doGet()、doPost())

    需要处理两种异常:ServletException(Servlet 处理异常)和 IOException(输入输出异常)

    通常不需要重写此方法,而是重写具体的请求处理方法

3. 请求处理方法
doGet () 方法

java 复制代码
public void doGet(HttpServletRequest request,HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}
  • 功能说明:

    专门用于处理 HTTP GET 类型的请求。

  • 适用场景:

    从服务器获取资源(如查询数据、页面跳转)

    请求参数会附加在 URL 后面,有长度限制

    不适合传输敏感信息(参数可见)

  • 典型应用:

    搜索查询、分页展示、资源下载等

doPost () 方法

java 复制代码
public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}
  • 功能说明:

    专门用于处理 HTTP POST 类型的请求。

  • 适用场景:

    向服务器提交数据(如表单提交、数据新增 / 修改)

    请求参数在请求体中,无明显长度限制

    相对安全,适合传输敏感信息(如用户名密码)

  • 典型应用:

    用户注册、登录验证、表单提交等

4. destroy () 方法

java 复制代码
public void destroy() {
    // 终止化代码...
  }

功能说明:
destroy() 是 Servlet 生命周期的最后一个方法,在 Servlet 被销毁前仅执行一次。

主要用途:

  • 释放资源,如关闭数据库连接、清理临时文件
  • 保存 Servlet 的状态信息

触发时机:

  • Web 应用被停止时
  • Servlet 容器(如 Tomcat)关闭时
  • Servlet 被从容器中移除时

3. 流程图


五、Servlet API

1. 接口定义的方法

Servlet 接口是 Jakarta Servlet 规范的核心,定义了所有 Servlet 组件必须遵循的基本方法和生命周期规范。任何 Servlet 类(包括 HttpServlet)都直接或间接实现了这个接口,它是 Servlet 容器Servlet 组件通信的桥梁。

java 复制代码
public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
 
    ServletConfig getServletConfig();
 
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
 
    String getServletInfo();
 
    void destroy();
}

2. Servlet API重要的接口与类​

①根基:顶层接口​​

​​Servlet接口​​:所有Servlet的​​根本契约​​。它定义了Servlet的生命周期方法:init(), service(), destroy()。任何Servlet都必须直接或间接实现它。

​​ServletConfig接口​​:代表一个Servlet的​​个体配置​​。提供了获取初始化参数和至关重要的 ​​ServletContext​​(应用上下文)的方法。

②中枢:通用实现类​​

​​GenericServlet抽象类​​:

​​关系​​:它​​实现​​了 Servlet接口和 ServletConfig接口。

​​职责​​:它是​​协议无关​​的通用实现。它为上述接口的大部分方法提供了默认实现,起到了适配器的作用,简化了开发。

③核心:HTTP专属实现类​​

​​HttpServlet抽象类​​:

​​关系​​:它​​继承​​自 GenericServlet 抽象类。

​​职责​​:它是专门为 ​​HTTP 协议​​定制的核心抽象类,也是开发者最常直接继承的类。其核心功能是​​重写​​了 service方法,根据HTTP请求方法(GET, POST等)将请求​​分发​​给对应的 doGet(), doPost()等方法。

​​④工具:请求与响应对象​​

​​ServletRequest/ ServletResponse(接口)​​:定义了通用的、协议无关的请求和响应功能。
​​HttpServletRequest/ HttpServletResponse(接口)​​:

​​关系​​:它们分别​​扩展​​(即继承)了 ServletRequestServletResponse接口。

​​职责​​:在通用功能之上,增加了​​HTTP协议特有​​的方法(如获取请求头、Cookie、Session,设置状态码、重定向等)。

​​协作关系​​:
HttpServlet​​处理​​ HttpServletRequest对象(从中获取客户端请求信息)。
HttpServlet​​创建​​并操作 HttpServletResponse对象(向其写入要返回给客户端的数据和信息)。

由此可见,只要是 Servlet 类,就有自己由容器管理的、规范的生命周期。​​

其核心在于顶层的 ​​Servlet接口​​,它就像一份"规定",明确规定了所有 Servlet 都必须具备的三个核心生命周期方法:

  • init(): ​​初始化​​方法,在 Servlet 被创建后立即调用,用于执行一次性性的设置工作。

  • service(): ​​核心服务​​方法,每次客户端请求到来时都会被调用,用于处理业务逻辑。

  • destroy(): ​​销毁​​方法,在 Servlet 被容器移除之前调用,用于释放资源和进行清理工作。

图中 GenericServlet抽象类和 HttpServlet抽象类都​​直接或间接地实现了 Servlet接口​​,这意味着它们"签署"了这份规定,承诺会拥有这些生命周期方法。

GenericServlet​​ 提供了这些生命周期方法的基础实现。

​​HttpServlet​​ 继承了 GenericServlet,并对其中的 service()方法进行了​​重写和扩展​​,加入了根据 HTTP 请求方法(GET, POST等)自动分发给 doGet(), doPost()等方法的逻辑。但这并未改变其生命周期的本质,只是让处理请求的环节变得更精细。

​​因此,结论是:​​

无论是一个最简单的直接实现 Servlet接口的类,还是最常用的继承 HttpServlet的类,​​它们都具备由 Web 容器(如 Tomcat)控制的、完整的生命周期(初始化、服务、销毁)​​。图中的所有类,正因为它们都是 Servlet接口谱系中的一员,所以都遵循这一规则。


3. Servlet接口使用演示(生命周期)

我们了解了Servlet的生命周期的原理之后,就可以自己来写一个Servlet的类了

这里我创建了一个新项目,包名为

java 复制代码
package com.demo1.lifecycle;

以下是代码片段,展示了Servlet完整的生命周期

java 复制代码
package com.demo1.lifecycle;

import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

// 配置Servlet并设置启动时加载(值越小优先级越高)
@WebServlet(
        urlPatterns = "/HelloWorld",
        loadOnStartup = 1  // 容器启动时就初始化,而不是首次请求时
)
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private String initMessage;  // 初始化参数示例

    // 1. 构造方法(容器通过反射调用,通常不重写)
    public HelloServlet() {  // 修正:构造方法不能有void返回值
        System.out.println("[Lifecycle] 1. Constructor called - Servlet instance created");
    }

    // 2. 初始化方法(仅执行一次)
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // 从配置中获取初始化参数(可在web.xml中配置)
        initMessage = config.getInitParameter("welcomeMessage");
        System.out.println("[Lifecycle] 2. init() method called - Initialization completed");
        System.out.println("   Initialization parameter value: " + initMessage);
    }

    // 3. 服务方法(每次请求都会执行,负责分发请求)
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("[Lifecycle] 3. service() method called - Start processing request");
        // 调用父类的service方法,自动分发到doGet/doPost
        super.service(request, response);
    }

    // 处理GET请求(实际业务逻辑)
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("[Lifecycle] 4. doGet() method called - Processing GET request");

        // 设置响应内容类型和编码
        response.setContentType("text/html;charset=UTF-8");

        // 获取输出流并输出内容
        try (PrintWriter out = response.getWriter()) {
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet Lifecycle Demo</title>");  // 页面标题改为英文
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Hello, World! (Jakarta EE)</h1>");  // 原内容保留(已为英文)
            out.println("<p>Initialization Info: " + initMessage + "</p>");  // 初始化信息改为英文
            out.println("<p>Current Thread: " + Thread.currentThread().getName() + "</p>");  // 当前线程改为英文
            out.println("</body>");
            out.println("</html>");
        }
    }

    // 5. 销毁方法(仅执行一次)
    @Override
    public void destroy() {
        System.out.println("[Lifecycle] 5. destroy() method called - Releasing resources");
        // 这里可以释放数据库连接、关闭文件等资源
        initMessage = null;  // 清空资源示例
    }
}

① 类定义与注解配置

java 复制代码
@WebServlet(
    urlPatterns = "/HelloWorld",
    loadOnStartup = 1  // 容器启动时就初始化,而不是首次请求时
)
public class HelloServlet extends HttpServlet { ... }
  • @WebServlet注解:替代传统的 web.xml 配置,声明这是一个 Servlet
    • urlPatterns = "/HelloWorld":指定访问路径,通过http://域名/项目名/HelloWorld可访问
    • loadOnStartup = 1:设置 Servlet 在容器启动时初始化(值为非负数),数值越小优先级越高
  • 继承HttpServlet:这是所有处理 HTTP 请求的 Servlet 的基类

② Servlet 生命周期方法解析

(1)构造方法

java 复制代码
public HelloServlet() {
    System.out.println("[Lifecycle] 1. Constructor called - Servlet instance created");
}
  • 由 Servlet 容器通过反射调用,创建 Servlet 实例
  • 仅在 Servlet 第一次被加载时执行一次
  • 注意:构造方法不能有返回值,代码中已修正了可能的错误

(2)初始化方法

java 复制代码
@Override
public void init(ServletConfig config) throws ServletException {
    super.init(config);
    initMessage = config.getInitParameter("welcomeMessage");
    System.out.println("[Lifecycle] 2. init() method called - Initialization completed");
}
  • 紧接着构造方法执行,也只执行一次
  • 用于初始化资源(如数据库连接、配置参数等)
  • 通过ServletConfig获取初始化参数(参数可在 web.xml 中配置)

(3)服务方法

java 复制代码
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("[Lifecycle] 3. service() method called - Start processing request");
    super.service(request, response);
}
  • 每次客户端请求都会执行
  • 负责请求分发:根据 HTTP 方法(GET/POST 等)调用对应的doXxx()方法
  • 这里调用了父类的service()方法,实现了自动分发功能

(4)处理 GET 请求

java 复制代码
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 设置响应内容类型和编码
    response.setContentType("text/html;charset=UTF-8");
    
    try (PrintWriter out = response.getWriter()) {
        // 输出HTML响应内容
        out.println("<h1>Hello, World! (Jakarta EE)</h1>");
        out.println("<p>Initialization Info: " + initMessage + "</p>");
    }
}
  • 专门处理 HTTP GET 请求的方法
  • response.setContentType():设置响应类型为 HTML 并指定编码为 UTF-8
  • 通过PrintWriter输出 HTML 内容到客户端浏览器
  • 使用 try-with-resources 语法自动关闭输出流

(5)销毁方法

java 复制代码
@Override
public void destroy() {
    System.out.println("[Lifecycle] 5. destroy() method called - Releasing resources");
    initMessage = null;  // 清空资源示例
}
  • 当 Servlet 容器关闭或应用被卸载时执行,仅执行一次
  • 用于释放资源,如关闭数据库连接、清理缓存等
  • 是 Servlet 生命周期的最后一个阶段

③ 其他重要说明

  • serialVersionUID:用于对象序列化版本控制,确保反序列化时的兼容性
  • 生命周期执行顺序:构造方法 → init() → service() → doGet()/doPost() → destroy()
  • Servlet 是单实例多线程的:一个 Servlet 类只有一个实例,容器会创建多个线程处理并发请求

这个示例完整展示了 Servlet 从创建、初始化、处理请求到销毁的整个生命周期

4. 运行结果

在访问以下网址时

bash 复制代码
http://localhost:8080/lifecycle_war_exploded/HelloWorld

控制台会输出以下内容

当我们关闭时

容器也会被销毁

相关推荐
_extraordinary_6 小时前
Java Servlet(二)--- HttpServlet,HttpServletRequest,HttpServletResponse
java·开发语言·servlet
TanYYF17 小时前
HttpServletRequestWrapper详解
java·servlet
猿事如此19 小时前
12-人事管理系统
mysql·servlet·jdbc·c3p0
步行cgn2 天前
HttpSessionBindingListener
java·开发语言·数据仓库·servlet
翻斗花园刘大胆3 天前
JavaWeb之HttpServletRequest与HttpServletResponse详解及快递管理系统实践
java·开发语言·数据库·mysql·servlet·架构·mvc
翻斗花园刘大胆3 天前
JavaWeb之快递管理系统(完结)
java·开发语言·前端·jvm·spring·servlet·java-ee
所愿ღ13 天前
JavaWeb-Servlet总结及JSP
java·笔记·servlet
所愿ღ13 天前
JavaWeb-Session和ServletContext
java·笔记·servlet
一勺菠萝丶13 天前
Jenkins 构建 Node 项目报错解析与解决——pnpm lockfile 问题实战
elasticsearch·servlet·jenkins