1. 概述

在本教程中,我们将使用 Servlet 3.0+ 构建一个 Java Web 应用。

重点介绍三个核心注解:@WebServlet@WebFilter@WebListener,它们可以完全替代传统的 web.xml 配置文件。

2. Maven 依赖

要使用这些新特性,我们需要引入以下依赖:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0-M1</version>
</dependency>

3. 基于 XML 的配置(传统方式)

在 Servlet 3.0 之前,Java Web 应用的配置是通过 web.xml 文件完成的:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">
    <listener>
        <listener-class>com.baeldung.servlets3.web.listeners.RequestListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>uppercaseServlet</servlet-name>
        <servlet-class>com.baeldung.servlets3.web.servlets.UppercaseServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>uppercaseServlet</servlet-name>
        <url-pattern>/uppercase</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>emptyParamFilter</filter-name>
        <filter-class>com.baeldung.servlets3.web.filters.EmptyParamFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>emptyParamFilter</filter-name>
        <url-pattern>/uppercase</url-pattern>
    </filter-mapping>
</web-app>

接下来,我们将用 Servlet 3.0 引入的注解逐步替代这些配置项。

4. Servlet 配置

从 JEE 6 开始,Servlet 3.0 支持使用注解定义 Servlet,从而减少对 web.xml 的依赖。

✅ 使用 @WebServlet 注解可以定义一个 Servlet 并绑定 URL 模式。

例如,我们定义一个用于 /uppercase 路径的 Servlet,它将请求参数 input 转为大写:

@WebServlet(urlPatterns = "/uppercase", name = "uppercaseServlet")
public class UppercaseServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws IOException {
        String inputString = request.getParameter("input").toUpperCase();

        PrintWriter out = response.getWriter();
        out.println(inputString);
    }
}

⚠️ 注意我们给 Servlet 指定了名称 uppercaseServlet,这个名称可以在其他地方引用(如 Filter 映射)。

使用 @WebServlet 注解,我们替代了 web.xml 中的 <servlet><servlet-mapping> 部分。

5. Filter 配置

Filter 是用来拦截请求或响应的对象,常用于预处理或后处理逻辑。

✅ 使用 @WebFilter 注解可以定义一个 Filter。

下面是一个用于检查 input 参数是否存在的 Filter:

@WebFilter(urlPatterns = "/uppercase")
public class EmptyParamFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
      FilterChain filterChain) throws IOException, ServletException {
        String inputString = servletRequest.getParameter("input");

        if (inputString != null && inputString.matches("[A-Za-z0-9]+")) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            servletResponse.getWriter().println("Missing input parameter");
        }
    }

    // 其他方法实现
}

通过 @WebFilter 注解,我们替代了 web.xml 中的 <filter><filter-mapping> 部分。

6. Listener 配置

Listener 用于监听特定事件(如请求创建/销毁),并执行我们自定义的逻辑。

✅ 使用 @WebListener 注解可以定义一个 Listener。

下面是一个监听请求销毁事件的 Listener,用于统计请求次数:

@WebListener
public class RequestListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent event) {
        HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
        if (!request.getServletPath().equals("/counter")) {
            ServletContext context = event.getServletContext();
            context.setAttribute("counter", (int) context.getAttribute("counter") + 1);
        }
    }

    // 其他方法实现
}

⚠️ 注意我们排除了 /counter 路径的请求,避免计数干扰。

通过 @WebListener 注解,我们替代了 web.xml 中的 <listener> 部分。

7. 构建与运行

测试时我们还添加了一个 /counter 接口,用于返回当前请求计数。

我们使用 Tomcat 作为应用服务器。

如果你使用的是 maven-war-plugin 3.1.0 之前的版本,需要设置:

<failOnMissingWebXml>false</failOnMissingWebXml>

然后将 .war 文件部署到 Tomcat,测试接口:

测试 /uppercase 接口

curl http://localhost:8080/spring-mvc-java/uppercase?input=texttouppercase

TEXTTOUPPERCASE

测试错误处理

curl http://localhost:8080/spring-mvc-java/uppercase

Missing input parameter

测试 Listener 统计

curl http://localhost:8080/spring-mvc-java/counter

Request counter: 2

8. 仍需使用 web.xml 的场景

虽然 Servlet 3.0 大幅减少了对 web.xml 的依赖,但在以下场景中仍然需要它:

无法通过注解定义 Filter 执行顺序
✅ 如果有多个 Filter 需要按顺序执行,仍需使用 <filter-mapping> 配置

❌ 定义 Session 超时时间 需要使用 <session-config>

❌ 容器级安全角色配置仍需 <security-role>

❌ 指定欢迎页面仍需 <welcome-file-list>

✅ Servlet 3.0 还提供了 **编程式配置接口 ServletContainerInitializer**,可用于弥补部分 XML 配置的缺失。

9. 总结

本教程展示了如何使用 Servlet 3.0+ 的注解替代 web.xml 来配置 Java Web 应用。

✅ 优点:

  • 配置更简洁
  • 更易维护
  • 代码即配置,减少配置文件冗余

❌ 局限:

  • 无法完全替代 XML(如 Filter 顺序)
  • 对于复杂项目,混合使用 XML 和注解更合理

源码地址:

如果你使用的是 Spring 框架,可以参考这篇教程:web.xml vs. Initializer with Spring


原始标题:A Java Web Application Without a web.xml

« 上一篇: RxJava Hooks 详解