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过滤器原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java类Circle定义计算圆的面积和周长代码示例

    java类Circle定义计算圆的面积和周长代码示例

    要用Java计算圆的周长和面积,需要使用圆的半径和一些数学公式,下面这篇文章主要给大家介绍了关于java类Circle定义计算圆的面积、周长的相关资料,需要的朋友可以参考下
    2024-04-04
  • SpringBoot如何集成Netty

    SpringBoot如何集成Netty

    这篇文章主要介绍了SpringBoot如何集成Netty问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Java终止循环体的具体实现

    Java终止循环体的具体实现

    这篇文章主要介绍了Java终止循环体的具体实现,需要的朋友可以参考下
    2014-02-02
  • Java中Integer类型值相等判断方法

    Java中Integer类型值相等判断方法

    这篇文章主要给大家介绍了关于Java中Integer类型值相等判断的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • SpringBoot项目保证接口幂等的五种方法

    SpringBoot项目保证接口幂等的五种方法

    在计算机领域中,幂等是指任意一个操作的多次执行总是能获得相同的结果,不会对系统状态产生额外影响,在Java后端开发中,幂等性的实现通常通过确保方法或服务调用的结果具有确定性,本文给大家介绍了SpringBoot项目保证接口幂等的五种方法,需要的朋友可以参考下
    2025-07-07
  • Java求质数的几种常用算法分析

    Java求质数的几种常用算法分析

    这篇文章主要介绍了Java求质数的几种常用算法,结合实例形式分析了三种比较常见的求质数算法原理及相关实现技巧,需要的朋友可以参考下
    2018-12-12
  • Java设计模式之策略模式(Strategy模式)介绍

    Java设计模式之策略模式(Strategy模式)介绍

    这篇文章主要介绍了Java设计模式之策略模式(Strategy模式)介绍,Strategy是属于设计模式中对象行为型模式,要是定义一系列的算法,这些算法一个个封装成单独的类,需要的朋友可以参考下
    2015-03-03
  • IDEA JeeSite框架httpSession.invalidate()无效问题解决方案

    IDEA JeeSite框架httpSession.invalidate()无效问题解决方案

    这篇文章主要介绍了IDEA JeeSite框架httpSession.invalidate()无效问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Java三种方法将List转换为Map的实例

    Java三种方法将List转换为Map的实例

    今天小编就为大家分享一篇关于Java三种方法将List转换为Map的实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • Java Integer.valueOf()和Integer.parseInt()的区别说明

    Java Integer.valueOf()和Integer.parseInt()的区别说明

    这篇文章主要介绍了Java Integer.valueOf()和Integer.parseInt()的区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08

最新评论