前言
过滤器是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过滤器原理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论