一. 过滤器概述
定义:
Filter,即过滤器,是JAVAEE技术规范之一,作为目标资源的请求进行过滤的一套技术规范,是JAVA WEB项目中最为实用的技术之一
。
- Filter 接口定义了过滤器的开发规范,所有的过滤器都要实现该接口(Filter)
- Filter 的工作位置是项目中所有目标资源之前,容器在创建
HttpServletRequest
和HttpServletResponse
对象后,会先调用Filter
的doFilter
方法 Filter
的doFilter
方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应- Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
- Filter是GOF中责任链模式的典型案例
- Filter的常用应用包括但不限于: 登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析… …
二. 应用场景
- 日志的记录
- 性能的分析
- 乱码的处理
- 事务的控制
- 登录的控制
- 跨域的处理
- ……
三. 过滤器工作位置图解

四. Filter接口API
1. 源码
1 2 3 4 5 6 7 8 9 10 11
| package jakarta.servlet; import java.io.IOException;
public interface Filter { default public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; default public void destroy() { } }
|
2. API目标
API | 目标 |
---|
default public void init(FilterConfig filterConfig) | 初始化方法,由容器调用并传入初始配置信息filterConfig对象 |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中 |
default public void destroy() | 销毁方法,容器在回收过滤器对象之前调用的方法 |
五. 过滤器的使用
目标:开发一个日志记录过滤器
步骤:
- 用户请求到达目标资源之前,记录用户的请求资源路径
- 响应之前记录本次请求目标资源运算的耗时
- 可以选择将日志记录进入文件,为了方便测试,这里将日志直接在控制台打印
1. 定义一个过滤器类,编写功能代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.nianxi.filters;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; public class LoggingFilter implements Filter {
private SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest) servletRequest; HttpServletResponse response =(HttpServletResponse) servletResponse; String requestURI = request.getRequestURI(); String time = dateFormat.format(new Date()); String beforeLogging =requestURI+"在"+time+"被请求了"; System.out.println(beforeLogging); long t1 = System.currentTimeMillis(); filterChain.doFilter(request,response); long t2 = System.currentTimeMillis(); String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒"; System.out.println(afterLogging);
} }
|
说明:
- doFilter方法中的请求和响应对象是
以父接口的形式声明的
,实际传入
的实参就是HttpServletRequest
和HttpServletResponse
子接口级别的,可以安全强转 - filterChain.doFilter(request,response); 这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止
- filterChain.doFilter(request,response);在放行时需要传入request和response,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象
2. 配置过滤器以及过滤器的过滤范围
a. xml配置文件配置
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0">
<filter> <filter-name>loggingFilter</filter-name> <filter-class>com.atguigu.filters.LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>/servletA</url-pattern> <url-pattern>*.html</url-pattern> <servlet-name>servletBName</servlet-name>
</filter-mapping> </web-app>
|
说明:
- filter-mapping标签中定义了过滤器对那些资源进行过滤
- 子标签url-pattern通过映射路径确定过滤范围
- /servletA 精确匹配,表示对servletA资源的请求进行过滤
- *.html 表示对以.action结尾的路径进行过滤
- /* 表示对所有资源进行过滤
- 一个filter-mapping下可以配置多个url-pattern
- 子标签servlet-name通过servlet别名确定对那些servlet进行过滤
- 使用该标签确定目标资源的前提是servlet已经起了别名
- 一个filter-mapping下可以定义多个servlet-name
- 一个filter-mapping下,servlet-name和url-pattern子标签可以同时存在
过滤过程图解

b. 注解方式配置(@WebFilter)
1. 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package jakarta.servlet.annotation;
import jakarta.servlet.DispatcherType; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebFilter { String description() default "";
String displayName() default "";
WebInitParam[] initParams() default {};
String filterName() default "";
String smallIcon() default "";
String largeIcon() default "";
String[] servletNames() default {};
String[] value() default {};
String[] urlPatterns() default {};
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
boolean asyncSupported() default false; }
|
2. 一个比较完整的Filter的XML配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <filter> <filter-name>loggingFilter</filter-name> <filter-class>com.atguigu.filters.LoggingFilter</filter-class> <init-param> <param-name>dateTimePattern</param-name> <param-value>yyyy-MM-dd HH:mm:ss</param-value> </init-param> </filter>
<filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>/servletA</url-pattern> <url-pattern>*.html</url-pattern> <servlet-name>servletBName</servlet-name> </filter-mapping>
|
3. 将xml配置转换成注解方式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.atguigu.filters;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date;
@WebFilter( filterName = "loggingFilter", initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")}, urlPatterns = {"/servletA","*.html"}, servletNames = {"servletBName"} ) public class LoggingFilter implements Filter { private SimpleDateFormat dateFormat ;
@Override public void init(FilterConfig filterConfig) throws ServletException { String dateTimePattern = filterConfig.getInitParameter("dateTimePattern"); dateFormat=new SimpleDateFormat(dateTimePattern); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest) servletRequest; HttpServletResponse response =(HttpServletResponse) servletResponse; String requestURI = request.getRequestURI(); String time = dateFormat.format(new Date()); String beforeLogging =requestURI+"在"+time+"被请求了"; System.out.println(beforeLogging); long t1 = System.currentTimeMillis(); filterChain.doFilter(request,response); long t2 = System.currentTimeMillis(); String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒"; System.out.println(afterLogging);
} }
|
六. 过滤器的生命周期
过滤器作为web项目的组件之一,和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造
阶段 | 对应方法 | 执行时机 | 执行次数 |
---|
创建对象 | 构造器 | web应用启动时 | 1 |
初始化方法 | void init(FilterConfig filterConfig) | 构造完毕 | 1 |
过滤请求 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) | 每次请求 | 多次 |
销毁 | default void destroy() | web应用关闭时 | 1次 |
Servlet的生命周期
实例化、初始化、处理请求、服务终止
实例化 —— 先创建servlet实例
- 当客户端首次发送第一次请求后,由Servlet容器去解析请求,根据请求找到是否有对应的servlet。
- 判断是否有Servlet实现类的对象存在?存在则直接使用,不存在则先创建一个servlet实现类的对象。
初始化 —— init()
调取init()方法进行初始化操作,可以在这一步中使用config.getInitParameter()方法调取配置文件中的参数,这一步在全生命周期内只执行一次。
处理请求 —— service()
初始化完成后调取service()方法,由service()判断客户端的请求方式。
- 如果是get请求,则执行doGet()方法。
- 如果是post请求,则执行doPost()。
- 处理方法完成后会作出相应的结果返回给客户端,单次请求处理完毕。
当用户发送第二次以后的请求时,会判断对象是否存在,但是不再执行init(),而直接执行service方法调取doGet() / doPost()方法。
服务终止 —— destroy()
当服务器关闭时Servlet调取destroy()方法进行销毁,宣告生命周期的结束。

过滤器test代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.atguigu.filters;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/*") public class LifeCycleFilter implements Filter { public LifeCycleFilter(){ System.out.println("LifeCycleFilter constructor method invoked"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("LifeCycleFilter init method invoked"); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("LifeCycleFilter doFilter method invoked"); filterChain.doFilter(servletRequest,servletResponse); }
@Override public void destroy() { System.out.println("LifeCycleFilter destory method invoked"); } }
|
七. 过滤器链
一个web项目中,可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称之为过滤器链
- 过滤器链中的过滤器的顺序由filter-mapping顺序决定
- 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
- 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
图解过滤器链

过滤器链功能测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atguigu.servlet;
import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletC") public class ServletC extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletC service method invoked"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Filter1 implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter1 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter1 after chain.doFilter code invoked");
} }
public class Filter2 implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter2 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter2 after chain.doFilter code invoked");
} }
public class Filter3 implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter3 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter3 after chain.doFilter code invoked");
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0"> <filter> <filter-name>filter1</filter-name> <filter-class>com.atguigu.filters.Filter1</filter-class> </filter>
<filter> <filter-name>filter2</filter-name> <filter-class>com.atguigu.filters.Filter2</filter-class> </filter>
<filter> <filter-name>filter3</filter-name> <filter-class>com.atguigu.filters.Filter3</filter-class> </filter>
<filter-mapping> <filter-name>filter1</filter-name> <url-pattern>/servletC</url-pattern> </filter-mapping>
<filter-mapping> <filter-name>filter2</filter-name> <url-pattern>/servletC</url-pattern> </filter-mapping>
<filter-mapping> <filter-name>filter3</filter-name> <url-pattern>/servletC</url-pattern> </filter-mapping>
</web-app>
|
工作流程图解
