1. 概述

Servlet 是运行在 Servlet 容器中的普通 Java 类,而 HTTP Servlet 作为其核心实现,在 Java Web 应用中扮演着关键角色。它的设计初衷就是围绕“请求-处理-响应”这一经典模型来处理 HTTP 请求,这也是客户端-服务器通信协议的基石。

除此之外,Servlet 还可以通过键值对形式的参数来控制客户端(通常是浏览器)与服务器之间的交互。这些参数分为两种作用域:

上下文参数(Context Parameters):应用级别,全局共享
Servlet 初始化参数(Servlet Initialization Parameters):仅限某个具体 Servlet 使用

本文将深入讲解如何定义和访问这两类参数,避免配置混乱的“踩坑”场景。


2. Servlet 初始化参数配置方式

Servlet 初始化参数可通过两种方式配置:

  • 使用注解(@WebServlet + @WebInitParam
  • 使用标准部署描述符 web.xml

⚠️ 两者可共存,但建议统一风格,避免维护混乱。

2.1 使用注解方式(Annotation-based)

使用注解的好处是:配置与代码紧耦合,修改方便,适合小型项目或微服务模块

下面我们通过一个简单的 UserServlet 示例来演示。

前端表单(JSP)

<!DOCTYPE html>
<html>
    <head>
        <title>Context and Initialization Servlet Parameters</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h2>Please fill the form below:</h2>
        <form action="${pageContext.request.contextPath}/userServlet" method="post">
            <label for="name"><strong>Name:</strong></label>
            <input type="text" name="name" id="name">
            <label for="email"><strong>Email:</strong></label>
            <input type="text" name="email" id="email">
            <input type="submit" value="Send">
        </form>
    </body>
</html>

📌 注意:${pageContext.request.contextPath} 是 EL 表达式,用于动态生成相对于应用上下文的 URL,避免硬编码路径。

UserServlet 实现

@WebServlet(name = "UserServlet", urlPatterns = "/userServlet", initParams={
    @WebInitParam(name="name", value="Not provided"), 
    @WebInitParam(name="email", value="Not provided")
})
public class UserServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
        forwardRequest(request, response, "/WEB-INF/jsp/result.jsp");
    }
    
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("name", getRequestParameter(request, "name"));
        request.setAttribute("email", getRequestParameter(request, "email"));
    }
    
    protected void forwardRequest(HttpServletRequest request, HttpServletResponse response, String path)
            throws ServletException, IOException { 
        request.getRequestDispatcher(path).forward(request, response); 
    }
    
    protected String getRequestParameter(HttpServletRequest request, String name) {
        String param = request.getParameter(name);
        return !param.isEmpty() ? param : getInitParameter(name);
    }
}

📌 核心点:

  • @WebInitParam 用于定义初始化参数,必须嵌套在 @WebServletinitParams 属性中。
  • getInitParameter(name) 是获取 Servlet 初始化参数的标准方法。
  • 若表单未填写字段,则回退到默认值(即初始化参数),实现“兜底逻辑”。

提交结果页面(result.jsp)

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>User Data</title>
    </head>
    <body>
        <h2>User Information</h2>
        <p><strong>Name:</strong> ${name}</p>
        <p><strong>Email:</strong> ${email}</p>
    </body>
</html>

📌 运行效果:

  • 用户填写表单 → 显示输入内容
  • 表单为空 → 显示初始化参数默认值
User Information
Name: John Doe
Email: john.doe@example.com

User Information 
Name: Not provided 
Email: Not provided

✅ 小结:注解方式简洁明了,适合快速开发,但不利于集中管理配置。


2.2 使用 web.xml 配置(Deployment Descriptor)

这种方式更“传统”,但优势在于:配置与代码分离,便于运维统一管理,适合大型项目或多环境部署

移除注解配置

先将 UserServlet 中的 initParams@WebInitParam 删除:

@WebServlet(name = "UserServlet", urlPatterns = {"/userServlet"}) 
public class UserServlet extends HttpServlet { ... }

在 web.xml 中定义参数

<?xml version="1.0" encoding="UTF-8"?>
<web-app  
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
  version="3.1">
    
    <servlet>
        <display-name>UserServlet</display-name>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.example.UserServlet</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>Not provided</param-value>
        </init-param>
        <init-param>
            <param-name>email</param-name>
            <param-value>Not provided</param-value>
        </init-param>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>
</web-app>

📌 关键标签说明:

  • <init-param>:包裹一个初始化参数
  • <param-name>:参数名
  • <param-value>:参数值

⚠️ 注意:即使使用注解声明了 @WebServlet,仍需在 web.xml 中显式声明 <servlet><servlet-mapping> 才能覆盖注解中的 initParams

✅ 效果与注解方式完全一致,只是配置位置不同。


3. 上下文参数(Context Parameters)配置

当某些数据需要在整个应用中共享且不可变时(如:系统名称、默认地区、API 密钥等),应使用 上下文参数(Context Parameters)

❌ 不能通过注解定义上下文参数,只能在 web.xml 中配置。

配置示例:设置默认地区

<web-app 
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
  version="3.1">
    
    <!-- 定义上下文参数 -->
    <context-param>
        <param-name>province</param-name>
        <param-value>Mendoza</param-value>
    </context-param>
    <context-param>
        <param-name>country</param-name>
        <param-value>Argentina</param-value>
    </context-param>

    <!-- 其他 servlet 配置 -->
    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.example.UserServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>
</web-app>

在 Servlet 中读取上下文参数

@WebServlet(name = "UserServlet", urlPatterns = {"/userServlet"})
public class UserServlet extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("name", getRequestParameter(request, "name"));
        request.setAttribute("email", getRequestParameter(request, "email"));
        request.setAttribute("province", getContextParameter("province"));
        request.setAttribute("country", getContextParameter("country"));
    }

    protected String getContextParameter(String name) {
        return getServletContext().getInitParameter(name);
    }

    // 其他方法略...
}

📌 核心方法:

  • getServletContext():获取应用级别的 ServletContext 对象
  • getInitParameter(name):从上下文中读取参数

更新 result.jsp 显示全局参数

<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Province:</strong> ${province}</p>
<p><strong>Country:</strong> ${country}</p>

📌 输出示例:

User Information 
Name: Alice Smith 
Email: alice.smith@example.com
Province: Mendoza
Country: Argentina

User Information 
Name: Not provided 
Email: Not provided
Province: Mendoza 
Country: Argentina

✅ 上下文参数的优势:

  • 跨多个 Servlet 共享
  • 应用启动时加载,不可变
  • 配置集中,便于维护

4. 总结

特性 Servlet 初始化参数 上下文参数
作用域 单个 Servlet 整个 Web 应用
配置方式 注解 ✅ 或 web.xml 仅 web.xml ✅
获取方式 getInitParameter() getServletContext().getInitParameter()
适用场景 Servlet 私有配置 全局共享配置

📌 关键结论:

  • 注解方式适合轻量级、快速迭代项目
  • web.xml 更适合复杂系统,尤其是需要多环境差异化配置时
  • 上下文参数必须用 web.xml 定义,这是当前 Servlet 规范的硬性要求
  • 参数命名建议统一前缀,避免冲突(如:app.timezoneuser.default.email

尽管现代框架(如 Spring Boot)已大幅减少 XML 使用,但在原生 Servlet 开发中,web.xml 依然不可替代,尤其是在定义全局上下文参数时。

📌 所有示例代码均可在 GitHub 获取:https://github.com/eugenp/tutorials/tree/master/web-modules/javax-servlets


原始标题:Context and Servlet Initialization Parameters