跳转到内容

Servlet

标签「Servlet」下的 1 篇文章

ServletConfig 接口介绍

这篇文章迁移自我早年写在博客园的一篇记录。当时主要是在学习 Servlet 初始化参数:Servlet 容器初始化 Servlet 时,会创建一个 ServletConfig 对象,并把它交给当前 Servlet 使用。

简单说,ServletConfig 适合保存“只属于某一个 Servlet 的配置”。比如某个 Servlet 自己需要的用户名、开关、路径、默认参数等。

当 Web 容器创建并初始化 Servlet 时,会为这个 Servlet 准备一个 ServletConfig 对象。这个对象里包含当前 Servlet 的初始化信息,也可以通过它拿到整个 Web 应用的 ServletContext

需要记住两点:

  • 一个 Web 应用里可以有多个 Servlet,也就可以有多个 ServletConfig 对象。
  • 一个 Servlet 只对应一个 ServletConfig 对象,所以 Servlet 初始化参数默认只对当前 Servlet 有效。

如果把 Web 应用理解成一个项目,那么:

  • ServletContext 更像“整个项目的全局上下文”。
  • ServletConfig 更像“某一个 Servlet 自己的配置说明”。

ServletConfig 常用方法不多,重点是下面这几个:

方法作用
String getInitParameter(String name)根据参数名获取当前 Servlet 的初始化参数值
Enumeration<String> getInitParameterNames()获取当前 Servlet 所有初始化参数名
ServletContext getServletContext()获取当前 Web 应用的 ServletContext 对象
String getServletName()获取当前 Servlet 名称,也就是 web.xml<servlet-name> 的值

这里最常用的是 getInitParameter()getInitParameterNames()。前者适合读取单个配置,后者适合遍历所有配置。

Servlet 里有两类参数很容易混在一起:

配置位置所属对象读取方式生效范围
<context-param>ServletContextgetServletContext().getInitParameter()整个 Web 应用
<servlet> 里的 <init-param>ServletConfiggetServletConfig().getInitParameter()当前 Servlet

也就是说,context-param 不是当前 Servlet 的私有配置,它属于整个 Web 应用。ServletConfig#getServletContext() 只是让你可以从当前 Servlet 拿到全局上下文,并不代表这些全局参数属于 ServletConfig

这个区别非常重要。很多初学 Servlet 的时候,会把“通过 ServletConfig 拿到 ServletContext,再读取全局参数”误认为是在读取 Servlet 自己的初始化参数。

先看全局参数的写法。下面的 admin-emailadmin-nameadmin-password 都配置在 <context-param> 中,因此它们属于整个 Web 应用。

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_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>main.java.com.Servlet</servlet-class>
</servlet>
<context-param>
<param-name>admin-email</param-name>
<param-value>123456@qq.com</param-value>
</context-param>
<context-param>
<param-name>admin-name</param-name>
<param-value>xiaoxi</param-value>
</context-param>
<context-param>
<param-name>admin-password</param-name>
<param-value>123456</param-value>
</context-param>
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/Servlet</url-pattern>
</servlet-mapping>
</web-app>

读取这些全局参数时,需要先拿到 ServletContext

Servlet.java
package main.java.com;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Servlet", value = "/Servlet")
public class Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain;charset=UTF-8");
ServletConfig config = getServletConfig();
ServletContext servletContext = config.getServletContext();
String adminEmail = servletContext.getInitParameter("admin-email");
String adminName = servletContext.getInitParameter("admin-name");
String password = servletContext.getInitParameter("admin-password");
response.getWriter().println("admin-email: " + adminEmail);
response.getWriter().println("admin-name: " + adminName);
response.getWriter().println("admin-password: " + password);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

这段代码里虽然先调用了 getServletConfig(),但真正读取参数的是 servletContext.getInitParameter()。所以它读取的是全局初始化参数。

使用 web.xml 配置当前 Servlet 参数

Section titled “使用 web.xml 配置当前 Servlet 参数”

如果希望参数只属于当前 Servlet,就应该把参数写到 <servlet> 里的 <init-param> 中。

web.xml
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>java.com.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>xiaoxi</param-value>
</init-param>
<init-param>
<param-name>admin</param-name>
<param-value>xiaoxi</param-value>
</init-param>
</servlet>

这种参数才是 ServletConfig 最典型的使用场景。

MyServlet.java
ServletConfig config = getServletConfig();
String name = config.getInitParameter("name");
String admin = config.getInitParameter("admin");

如果另一个 Servlet 也想使用同名参数,需要在另一个 Servlet 的配置里重新声明。Servlet 私有参数不会自动共享。

除了 web.xml,也可以直接使用 @WebServlet@WebInitParam 配置当前 Servlet 的初始化参数。

这种方式更适合简单项目或示例代码,因为配置和 Servlet 类写在一起,阅读起来更直观。

HelloServlet.java
package main.java.com;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
@WebServlet(
name = "helloServlet",
value = "/helloServlet",
initParams = {
@WebInitParam(name = "name", value = "测试"),
@WebInitParam(name = "admin", value = "123456")
}
)
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
ServletConfig config = getServletConfig();
String servletName = config.getServletName();
Enumeration<String> initParameterNames = config.getInitParameterNames();
PrintWriter writer = response.getWriter();
writer.write("servletName: " + servletName + "<br/>");
while (initParameterNames.hasMoreElements()) {
String initParamName = initParameterNames.nextElement();
String initParamValue = config.getInitParameter(initParamName);
writer.write(initParamName + ": " + initParamValue + "<br/>");
}
writer.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

这里的 initParams 配置只对 HelloServlet 生效。别的 Servlet 不能直接通过自己的 ServletConfig 读取这些参数。

如果项目很小,或者只是练习 Servlet,使用注解会比较方便。

如果项目配置比较多,或者需要把代码和配置分离,使用 web.xml 会更清晰。尤其是早期 Java Web 项目里,很多 Servlet、Filter、Listener 都会统一写在 web.xml 中。

可以简单按下面的方式判断:

场景推荐方式
示例代码、学习项目@WebServlet
配置较少的小项目@WebServletweb.xml 都可以
多个 Servlet 需要统一管理web.xml
希望参数不写死在 Java 类里web.xml

两种方式本质上都是告诉 Servlet 容器:这个 Servlet 叫什么、映射到哪个路径、初始化时带哪些参数。

第一,context-paraminit-param 不要混用。

如果一个参数是全站通用的,比如站点名称、管理员邮箱、上传目录,可以放在 <context-param> 中。如果一个参数只服务于某个 Servlet,就放在对应 Servlet 的 <init-param> 中。

第二,读取参数时要找对对象。

// 读取当前 Servlet 的初始化参数
getServletConfig().getInitParameter("name");
// 读取整个 Web 应用的全局参数
getServletContext().getInitParameter("admin-email");

第三,输出中文时记得设置响应编码。

response.setContentType("text/html;charset=UTF-8");

如果不设置编码,浏览器里可能会出现中文乱码。

第四,注意包名差异。

早期 Servlet 项目常见包名是 javax.servlet。如果使用的是 Tomcat 10、Spring Boot 3 或 Jakarta EE 新版本,包名会变成 jakarta.servlet

// 旧版本常见写法
import javax.servlet.ServletConfig;
// 新版本常见写法
import jakarta.servlet.ServletConfig;

学习旧项目或迁移项目时,这个差异很常见。

ServletConfig 的核心作用,是保存并读取当前 Servlet 的初始化参数。

需要特别分清楚:

  • ServletConfig 面向当前 Servlet。
  • ServletContext 面向整个 Web 应用。
  • <init-param> 是 Servlet 私有配置。
  • <context-param> 是 Web 应用全局配置。
  • 注解里的 initParams 只对当前 Servlet 生效。

把这几个概念理顺之后,Servlet 初始化参数就不难了。真正容易出错的地方,往往不是 API 本身,而是没有分清“这个配置到底属于谁”。

原文记录:ServletConfig接口介绍