【JavaWeb】过滤器 Filter

文章目录


过滤器是什么?

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

客户端发送请求 先经过filter的doFilter方法,这个方法控制是否放行请求到达目标,

不放行 即 使用filter的方式返回响应给客户端;

放行之后 请求即正常到达目标servlet

放行后 还可以对 响应数据进行处理


一、过滤器概述

  • Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口

  • Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequestHttpServletResponse对象后,会先调用FilterdoFilter方法

  • Filter的doFilter方法可以控制请求是否继续,

    • 如果放行,则请求继续,
    • 如果拒绝,则请求到此为止,由过滤器本身做出响应
  • Filter不仅可以对请求做出过滤 ,也可以在目标资源做出响应前 ,对响应再次进行处理

  • Filter是GOF中责任链模式的典型案例

  • Filter的常用应用包括但不限于: 登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析... ...

二、过滤器工作位置图解

三、Filter接口API

  • 源码
java 复制代码
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() {
    }
}
  • API
API 目标
default public void init(FilterConfig filterConfig) 初始化方法,由容器调用并传入初始配置信息filterConfig对象
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中
default public void destroy() 销毁方法,容器在回收过滤器对象之前调用的方法

四、过滤器使用

  1. 创建类 实现Filter接口
  2. 重写doFilter方法
  3. 可选择调用放行方法 将请求传下去 :
    3.1. filterChain.doFilter(servletRequest, servletResponse);
    3.2. 不写此方法 就是不放行请求到目标资源 卡这了

目标:开发一个日志记录过滤器

  • 用户请求到达目标资源之前,记录用户的请求资源路径
  • 响应之前记录本次请求目标资源运算的耗时
  • 可以选择将日志记录进入文件,为了方便测试,这里将日志直接在控制台打印

4.1 定义一个过滤器类,编写功能代码:

java 复制代码
package com.doug.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;

/**
 * @Description:
 */
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 req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //拼接日志
        String requestURI = req.getRequestURI();
        String time = dateFormat.format(new Date());
        String beforeLogging = requestURI + "在" + time + "被请求了";
        // 打印日志
        System.out.println(beforeLogging);

        // 获取系统时间
        long t1 = System.currentTimeMillis();
        //放行请求
        filterChain.doFilter(servletRequest, servletResponse);
        // 获取系统时间
        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);在放行时需要传入requestresponse,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象

4.2 xml配置:

xml 复制代码
    <!--配置filter,并为filter起别名-->
    <filter>
        <filter-name>loggingFilter</filter-name>
        <filter-class>com.doug.filters.LoggingFilter</filter-class>
    </filter>
    
    <!--为别名对应的filter配置要过滤的目标资源-->
    <filter-mapping>
        <filter-name>loggingFilter</filter-name>
        
		<!--  可以用获取servlet别名的方式      -->
        <servlet-name>sg1</servlet-name>
        
		<!--  或者 使用获取servletG 路径的方式      -->
     <url-pattern>/servletG</url-pattern>
     
	 <!-- 或者 通过后缀名确定过滤资源-->
        <url-pattern>*.html</url-pattern>
    </filter-mapping>

说明:

  • filter-mapping标签中定义了过滤器对那些资源进行过滤
  • 子标签url-pattern通过映射路径 确定过滤范围
    • /servletA 精确匹配,表示对servletA资源的请求进行过滤
    • *.html表示对以.action结尾的路径进行过滤
    • /* 表示对所有资源进行过滤
    • 一个filter-mapping下可以配置多个url-pattern
  • 子标签servlet-name通过servlet别名 确定对那些servlet进行过滤
    • 使用该标签确定目标资源的前提是servlet已经起了别名
    • 一个filter-mapping下可以定义多个servlet-name
    • 一个filter-mapping下,servlet-nameurl-pattern子标签可以同时存在

4.3 定义 servletG 目标资源 模拟测试 :

java 复制代码
@WebServlet(value = "/servletG",name = "sg1")
public class ServletG extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("servletG处理请求的方法,耗时10毫秒");
        System.out.println("servletG处理请求的方法,耗时10毫秒");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

4.4 过滤图解

五、过滤器生命周期

过滤器作为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次

六、过滤器链的使用

一个web项目中,可以同时定义多个过滤器 ,

多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,

称之为过滤器链

  • 过滤器链中的过滤器的顺序filter-mapping顺序决定
  • 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
  • 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低

6.1 图解过滤链

6.2 测试过滤链

  1. 定义servlet
java 复制代码
@WebServlet("/servletH")
public class ServletH extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletH service method  invoked");
    }
}
  1. 创建多个Filter
    三个过滤器代码
java 复制代码
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. xml 定义多个filtermapping 标签
java 复制代码
   <filter>
        <filter-name>filter1</filter-name>
        <filter-class>com.doug.filters.Filter1</filter-class>
    </filter>

    <filter>
        <filter-name>filter2</filter-name>
        <filter-class>com.doug.filters.Filter2</filter-class>
    </filter>

    <filter>
        <filter-name>filter3</filter-name>
        <filter-class>com.doug.filters.Filter3</filter-class>
    </filter>

    <!--filter-mapping的顺序决定了过滤器的工作顺序-->
    <filter-mapping>
        <filter-name>filter1</filter-name>
        <url-pattern>/servletH</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>filter2</filter-name>
        <url-pattern>/servletH</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>filter3</filter-name>
        <url-pattern>/servletH</url-pattern>
    </filter-mapping>

6.3 工作流程图解

七、注解方式配置过滤器

7.1 一个比较完整的Filter的XML配置

xml 复制代码
<!--配置filter,并为filter起别名-->
<filter>
    <filter-name>loggingFilter</filter-name>
    <filter-class>com.doug.filters.LoggingFilter</filter-class>
    <!--配置filter的初始参数-->
    <init-param>
        <param-name>dateTimePattern</param-name>
        <param-value>yyyy-MM-dd HH:mm:ss</param-value>
    </init-param>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
    <filter-name>loggingFilter</filter-name>
    <!--通过映射路径确定过滤资源-->
    <url-pattern>/servletA</url-pattern>
    <!--通过后缀名确定过滤资源-->
    <url-pattern>*.html</url-pattern>
    <!--通过servlet别名确定过滤资源-->
    <servlet-name>servletBName</servlet-name>
</filter-mapping>

7.2 将xml配置转换成注解方式实现

java 复制代码
@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 ;

    /*init初始化方法,通过filterConfig获取初始化参数
    * init方法中,可以用于定义一些其他初始化功能代码
    * */
    @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);

    }
}

总结

过滤器开发中应用的场景 :

  • 日志的记录
  • 性能的分析
  • 乱码的处理
  • 事务的控制
  • 登录的控制
  • 跨域的处理
  • ... ...

关于tomcat 10 在xml 配置 过滤器 导包是 jakarta 不是 'javax 会报红

但是可以正常使用...

不想爆红:

  1. 就降低 tomcat版本 为tomcat9

    主要是servlet-api 版本问题

  2. 添加依赖也可以

    因为以上没有使用maven 所以没有pom.xml添加依赖

相关推荐
Pandaconda5 分钟前
【计算机网络 - 基础问题】每日 3 题(十)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展
loveLifeLoveCoding7 分钟前
Java List sort() 排序
java·开发语言
草履虫·13 分钟前
【Java集合】LinkedList
java
AngeliaXue15 分钟前
Java集合(List篇)
java·开发语言·list·集合
世俗ˊ16 分钟前
Java中ArrayList和LinkedList的比较
java·开发语言
Mercury Random17 分钟前
Qwen 个人笔记
android·笔记
zhouyiddd21 分钟前
Maven Helper 插件
java·maven·intellij idea
攸攸太上29 分钟前
Docker学习
java·网络·学习·docker·容器
Milo_K37 分钟前
项目文件配置
java·开发语言
程序员大金41 分钟前
基于SpringBoot+Vue+MySQL的养老院管理系统
java·vue.js·spring boot·vscode·后端·mysql·vim