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 的执行顺序,从而确保业务逻辑的正确性与稳定性。

相关推荐
草莓熊Lotso3 分钟前
【C语言字符函数和字符串函数(一)】--字符分类函数,字符转换函数,strlen,strcpy,strcat函数的使用和模拟实现
c语言·开发语言·经验分享·笔记·其他
IT从业者张某某34 分钟前
信奥赛-刷题笔记-队列篇-T3-P3662Why Did the Cow Cross the Road II S
android·笔记
小秋学嵌入式-不读研版1 小时前
C42-作业练习
c语言·开发语言·笔记
田梓燊1 小时前
数学复习笔记 14
笔记·线性代数·矩阵
_Jyuan_2 小时前
尼康VR镜头防抖模式NORMAL和ACTIVE的区别(私人笔记)
经验分享·笔记·数码相机·相机
m0_678693332 小时前
深度学习笔记23-LSTM实现火灾预测(Tensorflow)
笔记·深度学习·lstm
ly_Enhs2 小时前
OCCT知识笔记之OCAF框架详解
笔记·occt知识笔记·occt知识点
忍者算法5 小时前
AWS VPC 核心笔记(小白向)
笔记·云计算·aws
北温凉5 小时前
【学习笔记】机器学习(Machine Learning) | 第七章|神经网络(1)
笔记·机器学习