一、过滤器(Filter)

1. 过滤器概述

过滤器是运行在服务器端的拦截器,它可以在请求到达 Servlet 之前 “拦截” 请求,也可以在响应返回给客户端之前 “拦截” 响应,从而实现统一的请求处理、权限控制、编码过滤等功能。

典型应用场景:

  • 统一设置请求 / 响应的编码格式。
  • 拦截非法请求(如未登录用户访问受保护页面)。
  • 记录请求日志、统计接口访问次数。

2. 过滤器入门代码

编写一个简单的编码过滤器,统一处理请求和响应的编码:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

// @WebFilter 注解配置过滤器的拦截路径
@WebFilter("/*") // 拦截所有请求
public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 过滤器初始化逻辑(仅执行一次)
        System.out.println("EncodingFilter 初始化完成");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 1. 预处理:设置编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        
        // 2. 放行请求(让请求继续到达目标Servlet)
        chain.doFilter(request, response);
        
        // 3. 后处理:可对响应做额外操作(如添加响应头)
        response.setHeader("X-Powered-By", "Java Web Filter");
    }

    @Override
    public void destroy() {
        // 过滤器销毁逻辑(仅执行一次)
        System.out.println("EncodingFilter 销毁");
    }
}

说明

  • init:过滤器初始化时执行,用于加载配置、创建资源等。
  • doFilter:每次请求被拦截时执行,是过滤器的核心方法。FilterChain.doFilter用于 “放行” 请求,若不调用则请求会被拦截。
  • destroy:过滤器销毁时执行,用于释放资源。

3. 过滤器执行流程

过滤器的执行流程可概括为:

  1. 客户端发送请求到服务器。
  2. 过滤器拦截请求,执行doFilter中的 “预处理” 逻辑。
  3. 调用chain.doFilter放行请求,请求到达目标 Servlet(或下一个过滤器)。
  4. Servlet 处理请求并生成响应。
  5. 响应回到过滤器,执行doFilter中的 “后处理” 逻辑。
  6. 响应最终返回给客户端。

4. 过滤器的拦截路径配置

过滤器通过@WebFilterurlPatterns属性配置拦截路径,支持多种匹配方式:

配置方式 示例 说明
精确匹配 @WebFilter("/user/login") 仅拦截/user/login请求
目录匹配 @WebFilter("/user/*") 拦截/user下的所有子路径
后缀匹配 @WebFilter("*.do") 拦截所有以.do结尾的请求
拦截所有请求 @WebFilter("/*") 拦截所有请求

5. 多个过滤器的执行顺序

当多个过滤器拦截同一请求时,执行顺序由过滤器类名的字母顺序决定(若需自定义顺序,可在web.xml中配置<filter-mapping>的顺序)。

示例:若有FilterAFilterB,类名FilterA字母序在前,则FilterA先执行,FilterB后执行;响应时则FilterB先处理,FilterA后处理(即 “先进后出”)。

二、监听器(Listener)

1. 监听器概述

监听器是监听 Web 应用中 “事件”的组件,它可以感知ServletContextHttpSessionServletRequest这三个域对象的创建、销毁或属性变化,从而实现全局事件处理、数据统计、资源初始化等功能。

根据监听的对象不同,监听器分为三类:

  • ServletContext 监听器:监听应用上下文的创建、销毁和属性变化。
  • HttpSession 监听器:监听会话(Session)的创建、销毁和属性变化。
  • ServletRequest 监听器:监听请求的创建、销毁和属性变化。

2. ServletContext 监听器

ServletContext是 Web 应用的全局上下文,对应的监听器接口是ServletContextListenerServletContextAttributeListener

(1)ServletContextListener(监听上下文的创建和销毁)

示例:在应用启动时初始化数据库连接池,关闭时销毁连接池。

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 应用启动时执行(服务器启动或应用部署时)
        System.out.println("应用启动,初始化数据库连接池...");
        // 此处编写初始化连接池的逻辑
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 应用销毁时执行(服务器关闭或应用卸载时)
        System.out.println("应用销毁,销毁数据库连接池...");
        // 此处编写销毁连接池的逻辑
    }
}

(2)ServletContextAttributeListener(监听上下文属性变化)

示例:监听ServletContext的属性新增、修改、删除。

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppContextAttrListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext 添加属性:" + scae.getName() + " = " + scae.getValue());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext 删除属性:" + scae.getName());
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext 修改属性:" + scae.getName() + " 旧值=" + scae.getValue());
    }
}

3. HttpSession 监听器

HttpSession是用户会话的载体,对应的监听器接口是HttpSessionListenerHttpSessionAttributeListener等。

(1)HttpSessionListener(监听会话的创建和销毁)

示例:统计在线用户数量。

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class SessionCountListener implements HttpSessionListener {
    private int onlineCount = 0;

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        onlineCount++;
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
        System.out.println("用户上线,在线人数:" + onlineCount);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        onlineCount--;
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
        System.out.println("用户下线,在线人数:" + onlineCount);
    }
}

(2)HttpSessionAttributeListener(监听会话属性变化)

示例:监听HttpSession的属性新增、修改、删除。

import javax.servlet.http.HttpSessionAttributeEvent;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class SessionAttrListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionAttributeEvent se) {
        System.out.println("Session 添加属性:" + se.getName() + " = " + se.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionAttributeEvent se) {
        System.out.println("Session 删除属性:" + se.getName());
    }

    @Override
    public void attributeReplaced(HttpSessionAttributeEvent se) {
        System.out.println("Session 修改属性:" + se.getName() + " 旧值=" + se.getValue());
    }
}

4. ServletRequest 监听器

ServletRequest是单次请求的载体,对应的监听器接口是ServletRequestListenerServletRequestAttributeListener等。

(1)ServletRequestListener(监听请求的创建和销毁)

示例:记录请求的创建和销毁时间。

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class RequestListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        sre.getServletRequest().setAttribute("requestStartTime", System.currentTimeMillis());
        System.out.println("请求创建");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        long startTime = (long) sre.getServletRequest().getAttribute("requestStartTime");
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("请求销毁,处理耗时:" + cost + "ms");
    }
}

三、过滤器与监听器的实战结合

案例:用户登录拦截与在线统计

需求:

  1. 未登录用户无法访问/user/*下的受保护页面(过滤器实现)。
  2. 统计当前在线用户数量(监听器实现)。

1. 登录过滤器(LoginFilter)

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebFilter("/user/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        
        // 判断用户是否登录(假设登录后Session中存在"user"属性)
        if (session.getAttribute("user") != null) {
            chain.doFilter(request, response); // 已登录,放行
        } else {
            resp.sendRedirect(req.getContextPath() + "/login.jsp"); // 未登录,跳转到登录页
        }
    }
}

2. 在线用户统计监听器(OnlineUserListener)

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class OnlineUserListener implements HttpSessionListener {
    private int onlineCount = 0;

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        onlineCount++;
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        onlineCount--;
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }
}

3. 页面展示在线人数

index.jsp中展示在线人数:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>在线统计</title>
</head>
<body>
    当前在线人数:${applicationScope.onlineCount}
</body>
</html>

 

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐