Filter

Filter

Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一

  • Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
  • Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法
  • Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应
  • Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
  • Filter是GOF中责任链模式的典型案例
  • Filter的常用应用包括但不限于: 日志的记录、性能的分析、乱码的处理、事务的控制、登录的控制、跨域的处理

在软件包下建立filters目录作为过滤器的文件夹

  • 继承Filter接口

  • 重写过滤方法(doFilter)

  • 配置过滤器

    • web.xml配置
    • 注解配置

配置过滤器

使用web.xml配置过滤器的过程大致与servlet相同

但是在mapping的位置上,过滤器有两种写法

<url-pattern>写法:
  • /* 过滤全部资源
  • /a/* 过滤以a开头的资源
  • *.html 过滤以html为后缀的资源
  • /ServletA 对ServletA的请求进行过滤(精确写法)
<filter>
    <filter-name>LoggingFilter</filter-name>
    <filter-class>com.xiaobai.filters.LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoggingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet-name>写法:

以xml文件的servlet-name别名或者以注解的name属性别名为参数,可以针对某个Servlet做出过滤

@WebServlet(value = "/ServletA",name = "ServletA") //注解的name属性别名
<filter>
    <filter-name>LoggingFilter</filter-name>
    <filter-class>com.xiaobai.filters.LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoggingFilter</filter-name>
    <servlet-name>ServletA</servlet-name>
</filter-mapping>

注:servlet-name 和 url-pattern都可存在多个,并且可以共存

注解的写法

Filter注解的写法与Servlet大致相同

@WebFilter("/*")

与web.xml相同,注解中也有servletNames属性

WebInitParam[] initParams() default {};

... ...

String[] servletNames() default {};

String[] value() default {};

String[] urlPatterns() default {};

我们可以使用WebInitParam[] initParams()注解属性对其初始参数进行配置


doFilter

请求到达目标资源之前,先经过该方法,该方法有能力控制请求是否继续向后到达目标资源,可以在该方法内直接向客户端做响应处理

请求在将目标资源相应回去之前,会再次经过该方法

  • 请求到达目标之前的功能代码
    • 判断是否登录
    • 校验权限是否满足
    • ……
  • 放行代码
  • HttpServletResponse在转换为响应报文之前的功能代码
package com.xiaobai.filters;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日志过滤器,记录请求的历史 将日志打印到控制台
 * 请求到达之前打印日志   yyyy-MM-dd HH:mm:ss
 * 请求结束之后打印访问资源的耗时 xxx资源在xxx的请求耗时x毫秒
 */
public class LoggingFilter implements Filter {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//使用dateFormate格式化时间

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //将请求相应的两个对象向下转型,以便能够使用HttpServletRequest和HttpServletResponse的方法
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //请求到达目标资源之前的功能代码(打印日志)
        String requestURI = req.getRequestURI();//获取URI
        String dataTime = simpleDateFormat.format(new Date());//获取系统时间
        String beforeLogging = requestURI + "在" + dataTime + "被访问了";
        System.out.println(beforeLogging);
        long t1 = System.currentTimeMillis();//获取资源前
        //放行代码
        filterChain.doFilter(servletRequest, servletResponse);

        long t2 = System.currentTimeMillis();//获取资源后
        //相应之前的功能代码
        String afterLogging = requestURI + "资源在" + dataTime + "的请求耗时:" + (t2 - t1) + "毫秒";
        System.out.println(afterLogging);
    }
}

注:我们写的过滤器直接实现了Filter的接口,形参对象是ServletRequest servletRequest, ServletResponse servletResponse

如果想要使用HttpServletRequest和HttpServletResponse的方法,需要向下转型 😶


生命周期

Filter的生命周期也与Servlet大致相同

步骤使用方法执行时间
实例化构造器第一次请求/随服务器启动
初始化init构造完毕
接受请求,处理请求,服务service每次请求
销毁destory关闭服务

不同的是,我们写的过滤器直接实现了Filter接口

Filter接口中的init和destory是使用了default修饰,即不重写也不会报错

并且Filter中没有无参构造方法


过滤器链

Filter可以存在多个,分别配置到不同的资源前,同一个资源可以有多个过滤器,将会形成过滤器链


web.xml配置多个过滤器

web.xml配置的过滤器链会按照<filter-mapping>配置的先后顺序执行,但响应的顺序是从后到前

<filter>
    <filter-name>Filter1</filter-name>
    <filter-class>com.xiaobai.filters.Filter1</filter-class>
</filter>
<filter-mapping>
    <filter-name>Filter1</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>Filter2</filter-name>
    <filter-class>com.xiaobai.filters.Filter2</filter-class>
</filter>
<filter-mapping>
    <filter-name>Filter2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>Filter3</filter-name>
    <filter-class>com.xiaobai.filters.Filter1</filter-class>
</filter>
<filter-mapping>
    <filter-name>Filter3</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

以上过滤器的执行流程是:

Filter1请求 -> Filter1放行 -> Filter2请求 -> Filter2放行 -> Filter3请求 -> Filter3放行 -> Filter3响应 -> Filter2响应 -> Filter1响应


注解的方式配置多个过滤器

通过注解配置的多个过滤器时,是通过类名决定过滤器链(过滤器顺序)

@WebFilter("/*")
public class Filter1 implements Filter {
}
@WebFilter("/*")
public class Filter2 implements Filter {
}
@WebFilter("/*")
public class Filter3 implements Filter {
}

Filter1请求 -> Filter1放行 -> Filter2请求 -> Filter2放行 -> Filter3请求 -> Filter3放行 -> Filter3响应 -> Filter2响应 -> Filter1响应