javax.servlet.Filter 介绍-笔记

1.javax.servlet.Filter 简介

javax.servlet.Filter 是 Java Servlet API 中的一个核心接口,用于在请求到达目标资源(如 Servlet 或 JSP)之前或响应返回给客户端之前执行预处理或后处理操作。它常用于实现与业务逻辑无关的通用功能,例如:

  • 日志记录:记录请求和响应的详细信息。
  • 身份验证和授权:检查用户权限,拦截未授权的访问。
  • 字符编码设置:统一处理请求和响应的字符编码。
  • 性能监控:统计请求的处理时间。
  • 请求/响应内容修改:例如压缩响应内容或修改请求头。

Filter 接口定义如下:

java 复制代码
package javax.servlet;

import java.io.IOException;

public interface Filter {
    void init(FilterConfig var1) throws ServletException;

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    void destroy();
}

Filter 接口包含以下三个方法:

  1. **init(FilterConfig config):**在 Filter 初始化时调用,用于读取 Filter 的初始化参数或执行其他初始化操作。

  2. **doFilter(ServletRequest request, ServletResponse response, FilterChain chain):**执行过滤逻辑的核心方法。开发者可以在此方法中:

    • 在调用 chain.doFilter() 之前执行前置处理(如权限检查)。
    • 调用 chain.doFilter() 将请求传递给下一个 Filter 或目标资源。
    • 在调用 chain.doFilter() 之后执行后置处理(如日志记录)。
  3. **destroy():**在 Filter 销毁时调用,用于释放资源或执行清理操作。

2. javax.servlet.Filter使用示例

2.1 代码示例

step1.定义Filter

定义一个用于记录请求信息的Filter:

java 复制代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作(可选)
        System.out.println("LoggingFilter initialized.");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // 前置处理:记录请求开始时间
        long startTime = System.currentTimeMillis();
        System.out.println("Request received: " + httpRequest.getRequestURI());

        // 继续处理链(调用下一个 Filter 或目标资源)
        chain.doFilter(request, response);

        // 后置处理:记录请求耗时
        long endTime = System.currentTimeMillis();
        System.out.println("Request completed in " + (endTime - startTime) + " ms");
    }

    @Override
    public void destroy() {
        // 清理操作(可选)
        System.out.println("LoggingFilter destroyed.");
    }
}

step2. 配置Filter

LoggingFilter必须 显式注册到 Web 应用中 才能生效, Spring Boot中注册Filter有两种方式:

方式1:使用 @Component(自动注册)

  • Spring Boot 会自动识别实现了 Filter 接口的 Bean,并将其注册为过滤器。
java 复制代码
import org.springframework.stereotype.Component;
import javax.servlet.Filter;

@Component
public class LoggingFilter implements Filter {
    // 上述 LoggingFilter 实现的 doFilter 等方法...
}

方式2: 手动注册为 FilterRegistrationBean

  • 对于有多个Filter的场景,该方式可以通过 FilterRegistrationBean.setOrder(int) 设置优先级(值越小越靠前)。
java 复制代码
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<TraceFilter> traceFilterRegistration() {
        FilterRegistrationBean<LoggingFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new LoggingFilter());
        registration.addUrlPatterns("/*"); // 拦截所有请求
        registration.setOrder(1); // 设置过滤器的顺序(可选)
        return registration;
    }
}

step3.测试 Filter

  1. 部署一个简单的 Servlet,并访问其 URL。
  2. 查看控制台输出,应能看到类似以下日志:
java 复制代码
LoggingFilter initialized.
Request received: /example/hello
Request completed in 5 ms

2.2 注意事项

  • Filter 顺序 :多个 Filter 的执行顺序可由 FilterRegistrationBean.setOrder(int) 设置值决定(值越小越靠前);响应时顺序相反。
  • 类型转换ServletRequestServletResponse 需要转换为 HttpServletRequestHttpServletResponse 以访问 HTTP 特定功能。
  • 异常处理 :在 doFilter 中抛出的异常会被 Servlet 容器捕获,需确保合理处理异常。

3.一个完整demo

在实际开发中,多个 Filter 的组合使用 是常见的需求,在 Spring Boot 中,通过 Filter 可以优雅地实现多个通用功能的串联处理。下面我们将通过一个 "身份认证+ trace追踪 + 请求日志记录" 的典型业务场景,展示如何在 Spring Boot 中配置并使用多个 Filter,并说明它们的执行顺序。

3.1 业务场景说明

我们构建一个 Web 应用,要求如下:

  1. 身份认证(AuthFilter):检查请求头中的 Authorization 字段,若缺失或无效,返回 401。
  2. trace追踪(TraceFilter):在请求中添加traceId,便于后续追踪该请求
  3. 请求日志记录(LoggingFilter):记录请求处理耗时、请求路径等信息。

Filter 执行顺序应为:
AuthFilter → TraceFilter → LoggingFilter

即:先进行身份验证,验证通过后,再添加traceId,最后进行日志记录。

3.2 完整代码

step1. 定义Filter

身份认证 Filter:

  • 这里顺便演示init() 方法的用法,可以通过init() 方法的参数 FilterConfig.getInitParameter获取在 FilterRegistrationBean.addInitParameter() 设置参数
java 复制代码
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AuthFilter implements Filter {
    private String authFilterParam;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化逻辑(可选)
        //在FilterConfig里配置Filter时,可以通过 registration.addInitParameter("paramName", "paramValue") 或 registrationBean.setInitParameters(params) 设置初始化参数
        authFilterParam = filterConfig.getInitParameter("AuthFilterParam");
        System.out.println("AuthFilter init param: " + authFilterParam);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpResponse = (HttpServletResponse)response;

        String authHeader = httpRequest.getHeader("Authorization");

        if (authHeader == null || !authHeader.equals("valid-token")) {
            // 鉴权失败,返回 401 Unauthorized; 这里演示方便直接打印一条日志
            //httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            //return;
            System.out.println("[AuthFilter] check authority failed");
        }

        // 继续处理请求
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 销毁逻辑(可选)
    }
}

trace追踪 Filter

java 复制代码
import java.io.IOException;
import java.util.UUID;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class TraceFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化逻辑(可选)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        // 生成唯一 traceId
        String traceId = UUID.randomUUID().toString();
        // 将 traceId 放入httpRequest
        httpRequest.setAttribute("traceId", traceId);

        // 继续处理请求
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 销毁逻辑(可选)
    }
}

请求日志记录 Filter

java 复制代码
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化逻辑(可选)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        long startTime = System.currentTimeMillis();
        System.out.println("[LoggingFilter] Request started: " + httpRequest.getRequestURI());
        System.out.println("[LoggingFilter] Request traceId: " + httpRequest.getAttribute("traceId"));

        // 继续处理请求
        chain.doFilter(request, response);

        long endTime = System.currentTimeMillis();
        System.out.println("[LoggingFilter] Request completed in " + (endTime - startTime) + " ms");
    }

    @Override
    public void destroy() {
        // 销毁逻辑(可选)
    }
}

step2. 配置Filter

使用Spring Boot 配置类,控制 Filter 顺序

java 复制代码
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<AuthFilter> authFilterRegistration() {
        FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>(new AuthFilter());
        registration.addUrlPatterns("/*");
        //配置AuthFilter init方法所需的参数,也可以通过registrationBean.setInitParameters(params)放一个map参数
        registration.addInitParameter("AuthFilterParam", "authFilterParamValue"); 
        registration.setOrder(1); // 优先级高,先执行
        return registration;
    }

    @Bean
    public FilterRegistrationBean<TraceFilter> traceFilterRegistration() {
        FilterRegistrationBean<TraceFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TraceFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(2); // 优先级中,执行
        return registration;
    }

    @Bean
    public FilterRegistrationBean<LoggingFilter> loggingFilterRegistration() {
        FilterRegistrationBean<LoggingFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new LoggingFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(3); // 优先级较低,后执行
        return registration;
    }
}

step3.定义测试controller

定义一个controller,用于测试Filter的效果:

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServletFilterController {
    @GetMapping("ServletFilterController/hello")
    public String hello() {
        return "hello world";
    }
}

step4. 测试

✅ 正常请求(带 Authorization)

  • 请求头:Authorization: valid-token
  • 预期输出:
java 复制代码
[LoggingFilter] Request started: /MdcTestController/hello
[LoggingFilter] Request traceId: 15b4852b-187b-42e0-a09b-98a34f17ae9b
[LoggingFilter] Request completed in 14 ms

❌ 未授权请求(无 Authorization)

  • 预期响应码:401 Unauthorized
  • trace和日志拦截器不会执行,不会有输出日志(因为请求在 AuthFilter 中被拦截)
  • 【备注】为方便演示,我们在AuthFilter里将鉴权失败逻辑中的代码改为输出日志了,所以未授权请求输出结果如下所示:

3.3 注意事项

  • 使用 FilterRegistrationBean 可以精确控制 Filter 的执行顺序,避免 Spring Boot 自动排序带来的不确定性。

  • 若 Filter 不需要作为 Spring Bean 管理,也可使用 @WebFilter + @Order,但不如 FilterRegistrationBean 灵活。
    *

    java 复制代码
    import javax.servlet.annotation.WebFilter;
    
    @WebFilter("/*") // 拦截所有请求
    public class LoggingFilter implements Filter {
        // ...(同上)
    }
  • 所有 Filter 默认对 /* 生效,可根据业务需求限定 URL 模式。


通过组合使用多个 Filter(身份认证 等),我们可以在 Spring Boot 中实现通用、可复用的前置/后置处理逻辑。借助 FilterRegistrationBean,我们可以清晰地控制 Filter 的执行顺序,从而确保业务逻辑的正确性与稳定性。

相关推荐
_Kayo_7 分钟前
node.js 学习笔记3 HTTP
笔记·学习
星星火柴9364 小时前
关于“双指针法“的总结
数据结构·c++·笔记·学习·算法
鱼鱼说测试4 小时前
Jenkins+Python自动化持续集成详细教程
开发语言·servlet·php
Cx330❀6 小时前
【数据结构初阶】--排序(五):计数排序,排序算法复杂度对比和稳定性分析
c语言·数据结构·经验分享·笔记·算法·排序算法
小幽余生不加糖7 小时前
电路方案分析(二十二)适用于音频应用的25-50W反激电源方案
人工智能·笔记·学习·音视频
..过云雨7 小时前
01.【数据结构-C语言】数据结构概念&算法效率(时间复杂度和空间复杂度)
c语言·数据结构·笔记·学习
岑梓铭8 小时前
考研408《计算机组成原理》复习笔记,第五章(3)——CPU的【数据通路】
笔记·考研·408·计算机组成原理·计组
Blossom.11814 小时前
把 AI 推理塞进「 8 位 MCU 」——0.5 KB RAM 跑通关键词唤醒的魔幻之旅
人工智能·笔记·单片机·嵌入式硬件·深度学习·机器学习·搜索引擎
草莓熊Lotso15 小时前
《吃透 C++ 类和对象(中):const 成员函数与取地址运算符重载解析》
c语言·开发语言·c++·笔记·其他
玖別ԅ(¯﹃¯ԅ)16 小时前
PID学习笔记6-倒立摆的实现
笔记·stm32·单片机