JavaWeb三大组件之一Filter(过滤器)

news2024/11/28 10:58:27

定义

拦截所有访问web资源的请求或者响应(servlet、Jsp页面、HTML页面),从而实现我们自己的业务逻辑,这些逻辑可以是实现访问权限的控制、过滤敏感词、压缩响应等功能。

原理 

过滤器是"链接"在容器的处理过程中的,它会在servlet处理器之前访问进入的请求,并且在响应信息返回客服端之前访问这些响应信息。这样就可以动态的修改请求和响应中的内容。

创建Filter

创建web项目:

 

或者像这样建立web项目:

 

 

  

添加Tomcat服务器:

 

如果没有SmartTomcat,只有Tomcat像下面图片中这样:

 

则选择Tomcat里面的local,并向下面这样配置:

 

 

 

启动程序:

 

 

 添加java包和servlet依赖:

创建过滤器并实现它的方法:

 

在web.xml中注册Filter: 

 

当我们在执行的时候,发现会报404错误,那是因为我们没有重写Filter里面的init方法,在用Filter的时候必须重写init方法,因为web 工程启动的时候执行构造器方法和init 初始化方法 ,如果不重写初始化方法,就不会走过滤器。

重写Filter里面的init方法,然后执行过滤器:

 

 

我们会发现在浏览器中什么都没有输出,但是控制台中输出了doFilter里面内容,说明我们的过滤器起作用了 ,他拦截了所有的请求。当我们在doFilter方法里面加上下面的逻辑,浏览器就会输出内容:

 

 

 

上面filterChain里面的doFilter方法它的作用就是请求放行到下一个资源,这个资源有可能是过滤器,也可能是客户端。

 

Filter的生命周期 

 

上面每一步分别对应不同的方法:

实例化对应Filter的构造器方法,初始化对应init()方法、销毁对应的是destroy()方法 

Filter它是一个接口,该接口里面有如下几个方法:

 

destroy()

当web服务器调用该方法时,表示过滤器将被销毁。

init()

当web服务器调用该方法时,表示过滤器将被注册到服务中

doFilter()

当服务器调用Filter中的doFilter()方法,它会将每一个请求或者响应传递给下一个资源

具体实现:

 

 

1.可以看到Filter是在应用启动是被创建和初始化的。

 

 

2.Filter是单例多线程的(创建和初始化只会被执行一次) 

3.doFilter()方法无论是那一个线程访问,只要由该Filter进行过滤,那么就会执行该Filter的doFilter()方法,并且是每过滤一次就会执行一次doFilter()。

 

4.Filter中destroy方法是在应用被停止时调用的,它意味着销毁这个Filter。 

5.由于Filter是单列多线程的,所以为了保证线程安全,不能在Filter中定义可修改的成员变量,因为每个线程均可修改这个成员变量,这样就会带来线程安全问题。 

FilterConfig 

FilterConfig指的是Filter在web.xml中的注册信息 ,它将注册信息进行封装,然后通过形参的方式传给初始化方法。

FilterConfig里面的方法:

 

演示使用:

package com.xihua;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;


public class MyFilter implements Filter {
    private FilterConfig filterConfig;
    public MyFilter() {
        System.out.println("执行了MyFilter的构造方法");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        System.out.println("执行了init方法");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //获取过滤器的名字
        String filterName = filterConfig.getFilterName();
        System.out.println("过滤器的名字:" + filterName);
        //获取过滤器中所有的初始化参数名称
        Enumeration<String> names = filterConfig.getInitParameterNames();
        while (names.hasMoreElements()){
            String name = names.nextElement();
            //获取过滤器中初始化参数对应的值
            String value = filterConfig.getInitParameter(name);
            System.out.println(name + "=" + value);
        }
        //获取全局域
        ServletContext sc = filterConfig.getServletContext();
        System.out.println("servletContext=" + sc);

        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("执行了destory方法");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.xihua.MyFilter</filter-class>
        <init-param>
            <param-name>school</param-name>
            <param-value>xihua</param-value>
        </init-param>
        <init-param>
            <param-name>researcher</param-name>
            <param-value>jack</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

运行结果:

 

<filter-mapping> 

<filter-mapping>标签表示拦截匹配,也就是要拦截那些请求。

<filter-name>:要拦截的过滤器名称

<url-pattern>:拦截那些路径

<url-pattern>

注意Filter中在写拦截所有路径的时候只能写成/*,而不能写成/,因为写成/它就不走拦截器中的doFilter方法了。 

在Servlet中/*即会拦截动态资源又会拦截静态资源,而/不会拦截动态资源

演示Filter拦截动态和静态资源如下:

package com.xihua;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;


public class MyFilter implements Filter {
    public MyFilter() {
        System.out.println("执行了MyFilter的构造方法");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了init方法");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行doFilter之前");
        System.out.println("执行doFilter之后");
    }

    @Override
    public void destroy() {
        System.out.println("执行了destory方法");
    }
}
package com.xihua.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("执行SomeServlet");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.xihua.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>SomeServlet</servlet-name>
        <servlet-class>com.xihua.servlet.SomeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SomeServlet</servlet-name>
        <url-pattern>/SomeServlet</url-pattern>
    </servlet-mapping>
</web-app>

 

 

  

当将Filter中的拦截改为/时,过滤器将不起作用:

 

下面演示Servlet/*和/的区别: 

 

  

 

 

在<filter-mapping>标签中可以不使用<url-pattern>标签,但是需要指定<servlet-name>,这样就能指定拦截某个servlet而不拦截其他请求:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.xihua.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <!--<url-pattern>/*</url-pattern>-->
        <servlet-name>SomeServlet</servlet-name>
    </filter-mapping>
    <servlet>
        <servlet-name>SomeServlet</servlet-name>
        <servlet-class>com.xihua.servlet.SomeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SomeServlet</servlet-name>
        <url-pattern>/SomeServlet</url-pattern>
    </servlet-mapping>
</web-app>

 

 

Filter里面的<dispatcher>标签

dispatcher表示分发器,表示过滤器所拦截的资源被servlet容器调用的方式,可以是REQUEST,INCLUDE,FORWARD,ERROR中的任何一个,默认是REQUEST。用户可以设置多个<dispatcher>子元素用来指定过滤器对资源的多种调用方式进行拦截。

FORWARD表示当前过滤器只会拦截由一个Servlet通过RequestDispatcher的forward()完成跳转
INCLUDE表示当前过滤器只会拦截由一个Servlet通过RequestDispatcher的include()完成跳转
REQUEST表示当前过滤器会拦截普通请求,但对于forward()与include()的跳转不进行拦截,REQUEST是默认的。
ERROR表示当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截

下面演示上面四个子元素的区别:

两个servlet:SomeServlet、OtherServelt

package com.xihua.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("执行SomeServlet");
        req.getRequestDispatcher("/otherServlet").forward(req,resp);
    }
}
package com.xihua.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class OtherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("执行了otherServlet");
    }
}

 在xml文件做如下配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--注册Filter-->
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.xihua.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--注册SomeServlet-->
    <servlet>
        <servlet-name>SomeServlet</servlet-name>
        <servlet-class>com.xihua.servlet.SomeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SomeServlet</servlet-name>
        <url-pattern>/someServlet</url-pattern>
    </servlet-mapping>
    <!--注册OtherServlet-->
    <servlet>
        <servlet-name>OtherServlet</servlet-name>
        <servlet-class>com.xihua.servlet.OtherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>OtherServlet</servlet-name>
        <url-pattern>/otherServlet</url-pattern>
    </servlet-mapping>
</web-app>

执行someServlet请求,因为在someServlet中我们设定了一个请求转发,观察拦截器的拦截效果:

 

再来看一下在<filter>标签里面添加<dispatcher>标签,并设定子元素为FORWARD:

    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

 

   

将请求转发的关键字换成include:

 

  

出现上面的原因是因为:

 

 

请求转发中forward和include的区别在于响应的标准输出流开启时间不一样,forward会请求传递给最后一个servlet,当最后一个servet执行完以后才会开启/返回响应。而include它是将其他请求包在第一个请求里面,并由第一个请求开启/返回响应。

 

Filter对请求和响应的修改 

 

 

 

 

多个Filter的执行顺序

两个Filter:MyFilter、UsFilter,在xml文件中先配置MyFilter,后配置UsFilter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--注册MyFilter-->
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.xihua.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--注册UsFilter-->
    <filter>
        <filter-name>UsFilter</filter-name>
        <filter-class>com.xihua.UsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--注册SomeServlet-->
    <servlet>
        <servlet-name>SomeServlet</servlet-name>
        <servlet-class>com.xihua.servlet.SomeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SomeServlet</servlet-name>
        <url-pattern>/someServlet</url-pattern>
    </servlet-mapping>

</web-app>

 

 

交换xml文件中的配置顺序:

 

总结:多个Filter的执行顺序,与Filter的配置顺序有关。 

 

Filter的执行原理 

Servlet的执行原理:在Servlet中有两个Map,这两个Map的key均为Servlet注册时的<url-pattern/>值,但value是不同的。第一个Map的value是Servlet实例对象的引用,第二个Map的value为<servlet-class/>的值,即Servlet类的全限定类名。

执行原理:

当对Servlet的请求到达Servlet容器时,会先对请求进行解析,使用该解析出的URL,作为比较对象,从第一个Map中查找是否有匹配的key,若不存在匹配的key,那么读取其value,即Servlet对象的引用,执行该Servlet的service()方法。

若不存在匹配的key ,那么再从第二个Map中查找是否有匹配的key。若存在,这读取其value,即读取其value,即要访问的Servlet的全限定类名。然后使用反射机制创建该Servlet实例,并将该实例写入到第一个Map中,然后在执行该Servlet的service()方法。

若第二个Map中也没有找到匹配的key,那么就跳转到错误处理页面404。

Filter的执行原理:

一个数组与一个Map :

一个Map:Map的key为<url-pattern/>的值,value为Filter实例对象的引用

一个数组:存在着与请求相匹配的所有Filter

执行原理:

当对某资源的请求到达Web容器时,会先对请求进行解析,使用解析出的URI作为比较对象,从Map中查找是否存在相匹配的key。若存在,那么读取其value,即Filter对象的引用,将该应用存入到数组中。然后继续向后查找,直到将Map查找完毕。这样在数组中就会存在按照查找顺序排好序的Filter引用。

数组初始化完毕后,开始按照数组元素顺序进行执行。所有数组中的Filter全部执行完毕后,再跳转到请求的目标资源。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/607548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023 华为 Datacom-HCIE 真题题库 09/12--含解析

单项选择题 1.[试题编号&#xff1a;190485] &#xff08;单选题&#xff09;华为交换机MAC地址表的老化时间默认是多少秒? A、500 B、5 C、300 D、400 答案&#xff1a;C 解析&#xff1a;无 2.[试题编号&#xff1a;190484] &#xff08;单选题&#xff09;如图所示&#…

小伙以1.2万元价格拍下一颗“荔枝王”:要将这颗最好的荔枝送给女友

近日&#xff0c;一则海南海口公益拍卖的新闻引起了网友的热议。 一位小伙子以1.2万元的价格拍下了一颗被誉为“荔枝王”的稀有果实&#xff0c;并表示要将这颗最好的荔枝送给自己的女友。 这个举动引起了一些网友的质疑&#xff0c;有人称赞他的浪漫&#xff0c;也有人质疑他的…

免费分享一套 SpringBoot + Vue的排课/选课管理系统,挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的SpringBoot Vue 的排课/选课管理系统&#xff0c;分享下哈。 项目介绍 近年来&#xff0c;随着网络学校规模的逐渐增大&#xff0c;人工书写数据已经不能够处理如此庞大的数据。为了更好的适应信息时代的高效性&#x…

Nginx网络服务——虚拟主机设置

Nginx网络服务——虚拟主机设置 一、基于域名的 Nginx 虚拟主机1&#xff0e;为虚拟主机提供域名解析2.为虚拟主机准备网页文档3.修改Nginx的配置文件4.重启服务&#xff0c;访问测试 二、基于IP 的 Nginx 虚拟主机1.设置虚拟主机IP2.修改主配置文件3.重启服务&#xff0c;访问…

chatgpt赋能python:Python反向传播算法——神经网络的基础

Python反向传播算法——神经网络的基础 什么是反向传播算法&#xff1f; 反向传播算法是神经网络中最常用的学习算法之一。它的作用是根据输入数据和输出数据来调整神经网络中的权重&#xff0c;从而让神经网络模型更好地适应特定的问题。 简单来说&#xff0c;反向传播算法…

强化学习Q-learning入门

1. 引言 本文为最近学习的强化学习Q-learning的学习笔记&#xff0c;主要用于总结和日常记录&#xff0c;本文主要讲解相应的必备入门知识。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 概念 我们小时候都经历过以下情形&#xff1a;我们做错了某年事&#xff0c;…

string

1.标准库的string类 构造函数&#xff1a; 构造函数的使用&#xff1a; 析构函数&#xff1a; 编译器会自己进行调用&#xff0c;不用管 拷贝构造函数&#xff1a; 追加字符函数&#xff1a; 追加字符串函数&#xff1a; 运算符重载函数&#xff1a; &#xff1a; 会用之后…

Python之socket编程(53)

​小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 今天的内容比较多哦&#xff0c;主要是网络编程的一些入门知识。 这些知识在互联网发展过程中很重要&#xff0…

Java中final关键字的理解(多线程中的final)

一、final关键字 Java中的final关键字具有不可变性的特性&#xff0c;其可以修饰类、方法和变量。 1. final修饰类&#xff1a;final关键字可以用来修饰类&#xff0c;表示该类是不可继承的。final类中的所有方法都是隐式final的&#xff08;不能被override或shadow&#xff0…

【深度学习-第1篇】深度学习是什么、能干什么、要怎样学?

大概4年前本专栏发布了一篇关于神经网络的入门科普文章&#xff0c;到现在大概有小5k赞了&#xff08;Mr.看海&#xff1a;神经网络15分钟入门&#xff01;足够通俗易懂了吧&#xff09;。 这几年人工智能发生了突飞猛进的发展&#xff0c;时至今日甚至发展出了GPT-4、Midjourn…

经典高并发服务器设计逻辑

都是面试必问的八股&#xff0c;不管理不理解用不用得上&#xff0c;背就完事了。 服务器模型 对于并发量比较大的服务器&#xff0c;即listen监听端口一直忙碌于处理新建连接的场景&#xff0c;一般在主线程里面accept新的客户端连接并生成新连接的socket&#xff0c;然后将这…

Unity制作二次元卡通渲染角色材质——2、色阶化光影的多种做法对比

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 这里继续讲二次元角色渲染。之前说过&#xff0c;最基本的卡通渲染&#xff0c;包含了色阶化光影和描边二个元素。所以这里先来说一下色阶化光影的多种做法对比。 一、光照模型和色阶化的说明 从上一篇文章里…

拿捏-哈夫曼树构建及编码生成(建议收藏)

文章目录 哈夫曼树的基本概念哈夫曼树的构建构建思路代码实现 哈夫曼编码的生成编码生成思路代码实现 完整代码展示及代码测试 哈夫曼树的基本概念 在认识哈夫曼树之前&#xff0c;你必须知道以下几个基本术语&#xff1a; 1、什么是路径&#xff1f; 在一棵树中&#xff0c;从…

Kafka 分区

分区是 Kafka 的核心功能&#xff0c;对于 Kafka 的存储结构、消息的生产消费方式都至关重要。 Partition&#xff08;分区&#xff09; 每 topic 都可以分成多分区&#xff0c;每个分区都是一组有序的、不可变的记录序列&#xff0c;每个分区在存储层面是 append log 文件。…

python中Requests发送json格式的post请求方法

问题&#xff1a;做requests请求时遇到如下报错&#xff1a; {“code”:“500”,“message”:"JSON parse error: Cannot construct instance of com.bang.erpapplication.domain.User (although at least one Creator exists): no String-argument constructor/factory …

唠一唠程序员的那些事

作为一名互联网摸爬滚打多年的老兵&#xff0c;我可以从以下角度提供一些信息&#xff1a; 加班是家常便饭&#xff1a;程序员往往需要加班来满足项目需求或完成任务。这意味着他们经常会在晚上、周末或节假日工作。代码不仅仅是工作&#xff1a;对于大多数程序员来说&#xff…

【C++】成员对象和成员函数分开存储

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、详解 3、代码清单 1 3.1、类中定义成员变量 3.2、类中定义成员函数 4、代码清单 2 5、总结 1、缘起 “成员变量…

SpringBoot 增强Controller方法,@ControllerAdvice注解的使用

参考资料 ControllerAdvice 用法SpringBoot使用ControllerAdvice注解 目录 一. ControllerAdvice注解作用二. ControllerAdvice注解 assignableTypes属性2.1 ControllerAdvice增强类2.2 Controller层2.3 效果 三. ControllerAdvice注解 basePackages属性3.1 ControllerAdvic…

踩坑|以为是Redis缓存没想到却是Spring事务!

前言 最近碰到了一个Bug&#xff0c;折腾了我好几天。并且这个Bug不是必现的&#xff0c;出现的概率比较低。一开始我以为是旧数据的问题&#xff0c;就让测试重新生成了一下数据&#xff0c;重新测试。由于后面几轮测试均未出现&#xff0c;我也就没太在意。 可惜好景不长&…

【LeetCode】260. 只出现一次的数字 III

260. 只出现一次的数字 III&#xff08;中等&#xff09; 思路 这道题是136. 只出现一次的数字 的进阶版&#xff0c;需要找出两个仅出现一次的元素。有了上一题的基础&#xff0c;我们很容易就想到要用异或来解决&#xff0c;但是由于这题最终会剩下两个不同的元素&#xff0…