Java Web过滤器的核心原理、实现与执行顺序配置方法(最新整理)

 更新时间:2025年12月04日 11:11:28   作者:ArabySide  
本文介绍了JavaWeb过滤器的核心概念、实现方式及执行顺序,过滤器允许在请求到达Servlet之前或响应返回客户端之前对请求和响应进行处理,感兴趣的朋友跟随小编一起看看吧

前言

过滤器是Java EE标准中Servlet规范提供的功能,也是传统JavaWeb的三大组件之一(Servlet,Filter和Listener)。通过过滤器可以把所有进入Servlet容器的请求都拦截住,无论静态资源还是Controller,从而实现一些通用的操作。

提示:以下是本篇文章正文内容,下面案例可供参考

一、核心功能

自定义过滤器的核心能力来自Filter这个接口。这个接口分别有init(),doFilter()和destroy()方法。init和destroy的生命周期依托于Servlet容器,容器初始化的时候会执行init()方法,容器销毁的时候会执行destroy()方法。

Filter 接口

package jakarta.servlet;
import java.io.IOException;
public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    default void destroy() {
    }
}

其中最常用的是doFilter方法,在每一次请求管道中,都会执行我们重写的这个方法。doFilter有一核心的FilterChain参数,它的核心作用是将请求传递给下一个 Filter或最终的控制器。缺少这个调用时,请求会卡在当前 Filter,后续所有处理环节都不会执行。
比如下面的doFilter方法中包含了对filterChain的调用,这样请求才会传递到后面,否则就会中断。

filterChain.doFilter(servletRequest, servletResponse);

另外doFilter方法中还有ServletRequest和ServletResponse这两个参数,这两个分别对应请求和响应。我们可以通过这两个参数来查看或设置请求/响应

二、实现方式

过滤器本身是Servlet规范提供的功能,在Spring Boot中实现一个过滤器主要通过两种方法,接下来通过两种方式实现一个字符格式化过滤器和日志打印过滤器。

以下两种写法其中本质上还是Spring Boot默认使用FilterRegistrationBean来注册自定义Filter到容器中,两者仅仅是写法上不同

2.1 @WebFilter + @ServletComponentScan

首先我们定义一个CharsetFilter类来实现Filter类,Filter功能由jakarta.servlet这个包提供提供。

注意在自定义类上要使用@WebFilter()注解修饰,里面的参数则是URI路径匹配通配符,这里设置为"/*",表示匹配所有。

CharsetFilter类主要是拦截住请求,该Filter接口里最为核心的方法是doFilter。我们在doFilter的重载里写上自定义的逻辑,比如这里是将客户端到服务器和服务器到客户端的数据流都设置为UTF-8的编码格式。

值得注意的是@WebFilter需要和@ServletComponentScan搭配使用。需要在启动类上通过@ServletComponentScan来控制Spring Boot启动的时候扫描到这个被@WebFilter修饰的自定义过滤器。

通过@WebFilter + @ServletComponentScan这种实现本质上是Spring引导 Servlet 容器扫描@WebFilter最终由Servlet 容器直接调用ServletContext.addFilter()完成过滤器的注册。

package org.araby.blognovelink.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
@WebFilter("/*")
public class CharsetFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化统一字符编码过滤器");
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("统一字符编码过滤器开始执行");
        servletRequest.setCharacterEncoding("UTF-8");
        servletResponse.setCharacterEncoding("UTF-8");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
        log.info("销毁统一字符编码过滤器");
        Filter.super.destroy();
    }
}
@SpringBootApplication
@ServletComponentScan
public class BlogNovelinkApplication {
    public static void main(String[] args) {
        SpringApplication.run(BlogNovelinkApplication.class, args);
    }
}

执行结果里包含

2.2 @Component修饰自定义过滤器类

第二种通过在自定义过滤器类添加@Component会更加的简单,这样就不需要在启动类上添加@ServletComponentScan。这种方式基于Spring的IOC容器,直接将过滤器作为Bean对象注册到容器中,最后在请求管道里注入自定义过滤器。
但仅用@Component注解标记Filter类时,Spring Boot 会自动注册该过滤器,默认拦截规则为所有请求。如果要手动控制匹配顺序,需要手动实现一个配置类来实现FilterRegistrationBean的注册,这里后面再详细介绍。

这里我们定义一个自定义的日志过滤器,这次只有在类上加一个@Component注解。其底层还是依赖 Spring的FilterRegistrationBean完成注册。

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
@Component
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化日志过滤器");
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("日志过滤器开始执行");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String uri = request.getRequestURI();
        String method = request.getMethod();
        String ip = request.getRemoteAddr();
        long startTime = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        // 记录响应耗时
        long costTime = System.currentTimeMillis() - startTime;
        log.info("Response - URL: {}, Cost Time: {}ms", uri, costTime);
    }
    @Override
    public void destroy() {
        log.info("销毁日志过滤器");
        Filter.super.destroy();
    }
}

三、过滤器执行的顺序

3.1 现象讨论

假想一下有三个过滤器在请求管道里,分别是过滤器a,过滤器b和过滤器c。它们都是通过@WebFilter + @ServletComponentScan的方式初始化。

观察执行结果发现了一个神奇的现象,初始化和销毁顺序 不等于执行顺序。初始化/销毁顺序是A-C-B,而 执行顺序是A-B-C。这是因为@WebFilter + @ServletComponentScan默认情况下自定义过滤器其初始化顺序由Servlet容器加载Servlet组件的规则决定,而执行顺序由Servlet容器构建FilterChain时的排序规则,像Tomcat默认会对所有@WebFilter按类名排序。

执行结果

[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.AFilter - 初始化A过滤器
[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.CFilter - 初始化C过滤器
[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.BFilter - 初始化B过滤器
[INFO ] [2025-12-02 22:14:36.764] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.AFilter - A过滤器开始执行
[INFO ] [2025-12-02 22:14:36.765] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.BFilter - B过滤器开始执行
[INFO ] [2025-12-02 22:14:36.765] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.CFilter - C过滤器开始执行
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.AFilter - 销毁A过滤器
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.CFilter - 销毁C过滤器
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.BFilter - 销毁B过滤器

3.2 自定义顺序

既然都已经是在Spring Boot中使用过滤器,那么我们就应该用Spring的方式管理Filter,而不是依赖 Servlet容器的隐式行为。这里其实是采用FilterRegistrationBean来手动定义注册的顺序,所以过滤器类可以不用@WebFilter + @ServletComponentScan或者@Component修饰。启动类上也不需要@ServletComponentScan。

这样注册过滤器的的过程由Spring Boot通过FilterRegistrationBean统一管理,而非直接依赖Servlet 容器的注解扫描。

这里我们定义一个配置类WebConfig ,用@Configuration注解修饰。里面手动实现FilterRegistrationBean来管理定义的过滤器。

  • setFilter:添加过滤器类
  • addUrlPatterns:添加匹配路径
  • setOrder:指定顺序,数值越小,过滤器doFilter越先执行
import org.araby.blognovelink.filter.AFilter;
import org.araby.blognovelink.filter.BFilter;
import org.araby.blognovelink.filter.CFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean<AFilter> aFilter() {
        FilterRegistrationBean<AFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new AFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(1); // 最先执行
        return bean;
    }
    @Bean
    public FilterRegistrationBean<BFilter> bFilter() {
        FilterRegistrationBean<BFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new BFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(2);
        return bean;
    }
    @Bean
    public FilterRegistrationBean<CFilter> cFilter() {
        FilterRegistrationBean<CFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new CFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(3); // 最后执行
        return bean;
    }
}

到此这篇关于Java Web过滤器的核心原理、实现与执行顺序配置的文章就介绍到这了,更多相关javaweb过滤器原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot整合liteflow的实现示例

    springboot整合liteflow的实现示例

    本文主要介绍了在Spring Boot项目中整合Liteflow规则引擎,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • MyBatis trim标签核心用法代码实战

    MyBatis trim标签核心用法代码实战

    MyBatis的<trim>标签用于动态SQL拼接,处理SQL前后缀和多余关键字,避免语法错误,提升代码可维护性,本文给大家介绍MyBatis trim标签核心用法代码实战,感兴趣的朋友跟随小编一起看看吧
    2026-03-03
  • idea生成类注释和方法注释的正确方法(推荐)

    idea生成类注释和方法注释的正确方法(推荐)

    这篇文章主要介绍了idea生成类注释和方法注释的正确方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • IDEA中Jar包的制作和使用方法

    IDEA中Jar包的制作和使用方法

    这篇文章主要介绍了IDEA的Jar包的制作和使用方法,文中补充介绍了idea的pom依赖配置后,不会自动下载jar包的解决办法,感兴趣的朋友跟随小编一起看看吧
    2025-04-04
  • maven使用systemPath方式加载本地jar的实现

    maven使用systemPath方式加载本地jar的实现

    本文介绍Maven通过systemPath加载本地第三方JAR的方法,需配置依赖和插件以确保打包和运行,具有一定的参考价值,感兴趣的可以了解一下
    2025-06-06
  • Spring 中 PageHelper 不生效问题及解决方法

    Spring 中 PageHelper 不生效问题及解决方法

    这篇文章主要介绍了Spring 中 PageHelper 不生效问题,使用这个插件时要注意版本的问题,不同的版本可能 PageHelper 不会生效,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • Java实现动态生成GIF图像详解

    Java实现动态生成GIF图像详解

    在互联网上有许多有趣的场景,其中的一种就是动图。这不是视频,而是一种GIF图像信息。本文将利用Java实现动态生成GIF图像功能,需要的可以参考一下
    2022-09-09
  • WeakHashMap的使用方法详解

    WeakHashMap的使用方法详解

    这篇文章主要介绍了WeakHashMap的使用方法详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • SpringMVC使用@ExceptionHandler注解在Controller中处理异常

    SpringMVC使用@ExceptionHandler注解在Controller中处理异常

    这篇文章主要为大家介绍了SpringMVC使用@ExceptionHandler注解在Controller中处理异常示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Java for循环的妙用之鸡兔同笼问题

    Java for循环的妙用之鸡兔同笼问题

    这篇文章主要给大家介绍了关于Java for循环的妙用之鸡兔同笼问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论