• Filter(过滤器)的使用


    1、Filter简介

           Filter称之为过滤器。我们生活中的过滤器有过滤网、净水器、空气净化器等。而Web中过滤器是用来对Web服务器管理的Web资源进行拦截(如JSP, Servlet, 静态图片文件或静态html文件等),从而完成一些特殊的功能。比如:实现URL级别的权限访问控制(最常用)、字符编码、登录限制、过滤敏感词汇、文件压缩,跨域设置等一些高级功能。

           在Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

    image

        上面图片的理解就是:当一个请求过来时,首先会被过滤器拦截下来,若满足条件,则进入下一步,执行完毕之后,又返回Filter,最后再返回给客户端。若不满足条件,则直接返回。

    2、Filter开发步骤

    Filter开发步骤很简单,分为两个步骤:

    1. 编写Java类实现Filter接口,并实现其doFilter方法(也以重写init方法与destroy方法)。
    2. 在web.xml 文件中使用<filter>和<filter-mapping>元素配置filter,并设置它所能拦截的资源。当然我们也可以通过注解来实现。

    下面举个简单的例子来说明下:字符编码拦截。我还记的在之前的博客中,如果要把含有中文的数据输出到网页中而不出现乱码的话,就必须在代码中下面这些信息。

    request.setCharacterEncoding("UTF-8");
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html");

    如果页面不多的话还好,如果页面多了的话,就要写很多重复。而用过滤器只需写一遍即可。


    Filter代码示例:

    package com.thr;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    /**
     * @author tanghaorong
     * @date 2020-05-02
     * @desc  创建一个实现Filter的实现类
     */
    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("Filter初始化,只初始化一次...");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    
            //对request和response进行一些预处理
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
    
            System.out.println("拦截前执行...");
            filterChain.doFilter(request,response);
            System.out.println("拦截后执行...");
        }
    
        @Override
        public void destroy() {
            System.out.println("Filter销毁...");
        }
    }
    

       

        在web. 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-name>MyFilter</filter-name>
               <filter-class>com.thr.MyFilter</filter-class>
           </filter>
    
           <!--映射过滤器-->
           <filter-mapping>
               <filter-name>MyFilter</filter-name>
               <!--/* :表示拦截所有的请求 -->
               <url-pattern>/*</url-pattern>
           </filter-mapping>
    
    </web-app>
    

    拦截的规则和Servlet的匹配规则一样,它们的拦截规则如下:

    1. 以指定资源匹配。例如:/index.jsp、/login
    2. 以目录匹配。例如:/servlet/* 、 /abc/def
    3. 以后缀名匹配,例如:*.jsp 、 *.do
    4. 通配符,拦截所有web资源:/*

    注意的点也是一样的:/abc/*.do、/*.do、abc*.do 这些都是非法的,这样的拦截规则是无效的。

    我们也可以为一个过滤器配置多个映射,也就是一个Filter对应多个<filter-mapping>。但是这样设计好像没什么用,因为只要有一个URL匹配就会被拦截。

    上面是对于单个Filter配置多个mapping的情况,而后面还有多个Filter对应多个mapping,这种多个Filter组合起来就称之为一个Filter链,此时的Filter的执行顺序是不确定的。因为这个地方难以理解,所以后面会详细介绍。


        当然我们也可以不在web.xml中配置,可以使用注解——@WebFilter。@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解包含的所有属性如下:

    属性名 类型 描述
    filterName String 指定过滤器的 name 属性,等价于 <filter-name>
    value String[] 该属性等价于 urlPatterns 属性。但是两者不应该同时使用。
    urlPatterns String[] 指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签。
    servletNames String[] 指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 <servlet-name> 的取值。
    dispatcherTypes DispatcherType 指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
    initParams WebInitParam[] 指定一组过滤器初始化参数,等价于 <init-param> 标签。
    asyncSupported boolean 声明过滤器是否支持异步操作模式,等价于 <async-supported> 标签。
    description String 该过滤器的描述信息,等价于 <description> 标签。
    displayName String 该过滤器的显示名,通常配合工具使用,等价于 <display-name> 标签。
    smallIcon String 此Filter的小图标。
    largeIcon String 此Filter的大图标。

           使用@WebFilter的一个简单举例,还是用上面MyFilter过滤器。不过在测试的发现了一个问题,就是使用了注解配置Filter,但是继续在web.xml文件中配置该Filter(配置信息一样),好像是不会报错的。这个我也不知道是怎么回事,每种方式我都去试了一下,结果不会出现问题,可能过滤器是支持这种方式的吧,但是Servlet就不行会报错。(如果有大神知道可以多多指出,毕竟我还是个菜鸟)

    /**
     * @author tanghaorong
     * @date 2020-05-03
     * @desc  使用注解配置Filter
     */
    @WebFilter(filterName = "myFilter",value = "/*")
    public class MyFilter implements Filter {
        
        code...
        
    }

           上面使用注解和web.xml同时配置不报错的原因是:服务器在启动时首先是会加载web.xml的文件,Filter的映射据<filter-mapping>的顺序来映射的,由于上面的代码使用注解和web.xml同时配置了同一个Filter,相当于配置两次<filter>和<filter-mapping>。故这个Filter会初始化两次,由于先加载的web.xml文件,所以当@WebFilter设置的过滤器被初始化时,它得到的字符串为空。

    3、Filter的生命周期

    在Filter接口中定义了三个方法,这三个都是与Filter的生命相关的方法,我们看一下源码:

    package javax.servlet;
    
    import java.io.IOException;
    
    public interface Filter {
        void init(FilterConfig var1) throws ServletException;
    
        void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    
        void destroy();
    }

    对于这三个方法的介绍:

    1. init(FilterConfig var1):表示Filter对象的初始化方法,在Filter对象创建时执行(只执行一次),并且传入一个FilterConfig类型的参数,该参数封装了Filter的<init-param>初始化参数。
    2. doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3):表示Filter执行过滤的核心方法,如果某资源在已经被配置到这个Filter进行过滤的话,那么每次访问这个资源都会执行doFilter方法,注意 这里的request和response没有http需要强制转换。
    3. destory():表示Filter销毁方法,当Filter对象销毁时执行该方法(仅执行一次)。

    4、多个过滤器的执行顺序

           如果在一个Web应用中,编写了多个Filter,那么这些Filter组合起来称之为一个Filter链。此时的Web服务器根据Filter在web.xml文件中的<filter-mapping>定义顺序执行。如果是注解的话则根据类名先后顺序执行。如果web.xml和注解混合使用,则先加载web.xml然后注解。当第一个Filter的doFilter方法被调用时,Web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则Web服务器会检查FilterChain对象中是否还有Filter,如果还有,则调用第2个Filter,依次类推,直到没有可以调用目标资源。


    我们举个例子好好理解一下。分别创建A、B、C、D  四个Filter。其中A和D过滤器用注解配置,B和C过滤器用web.xml配置。

    AFilter和DFilter代码示例如下:(因为代码大致相同,所以我就贴一个,就类名和打印时的名称不一样罢了。如果使用web.xml配置就把注解注释掉即可)

    package com.thr;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    import java.io.IOException;
    import java.util.Enumeration;
    
    /**
     * @author tanghaorong
     * @date 2020-05-03
     * @desc 使用注解配置AFilter
     */
    @WebFilter(filterName = "AFilter",value = "/*")
    public class AFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("初始过滤器名称:AFilter");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("AFilter拦截前执行...");
            filterChain.doFilter(request,response);
            System.out.println("AFilter拦截后执行...");
        }
    
        @Override
        public void destroy() {
            System.out.println("AFilter销毁...");
        }
    }


    BFilter和CFilter在web.xml的配置如下:(注意:执行顺序跟<filter>的顺序无关。而是根据<filter-mapping>的顺序决定执行顺序)

    <?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>CFilter</filter-name>
                <filter-class>com.thr.CFilter</filter-class>
            </filter>
            <filter>
                <filter-name>BFilter</filter-name>
                <filter-class>com.thr.BFilter</filter-class>
            </filter>
    
            <!--映射过滤器-->
            <!--注意:这里CFilter在BFilter之前-->
            <filter-mapping>
               <filter-name>CFilter</filter-name>
               <url-pattern>/*</url-pattern>
            </filter-mapping>
    
            <filter-mapping>
                <filter-name>BFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
    
    </web-app>
    


       运行后的结果如下:

       这是初始化的顺序截图:

    image

       每次运行的结果都是这个,不知道过滤器初始化创建的顺序根据什么来的执行的?不过这不是重点,重点在下面。


       这是过滤器运行顺序截图:

    image

       可以看到,过滤器的执行结果符合我们的预期。CFilter和BFilter是在web.xml中声明的,且CFilter的定义在BFilter之前,所以CFilter在BFilter之前执行。而注解则是根据类来执行的。

       而且可以发现每当过滤器调用filterChain.doFilter(request,response)之后,检查到后面还有过滤器,继续执行后面的过滤器,当检查到没有过滤器后,在依次执行过滤器后面的操作。


       这是过滤器销毁顺序截图:

    image

       执行顺序和初始化是一样的。


       我后面又试了试很多种方法,注解和web.xml混合配置,全注解配置,全web.xml配置。

       可以的出一个结论:web.xml方式是根据mapping中先后顺序执行。而注解方式则是根据类名先后顺序排序的。但是Filter初始化和销毁时的顺序我也不知道是根据什么排的序。

    5、FilterConfig对象的使用

           还记得在Servlet中有个ServletConfig,它代表的是当前Servlet的一些初始化参数。所以FilterConfig也是一样的,当我们在配置Filter的时候,使用<init-param>为Filter配置一些初始化参数后,在Filter对象被创建并且调用其init方法时,会把封装了Filter初始化参数的FilterConfig对象传递进来。因此通过FilterConfig对象的方法就可获取初始化配置信息。方法如下:

    1. String getFilterName():返回Filter的名称。
    2. String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
    3. Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
    4. ServletContext getServletContext():返回Servlet上下文对象的引用。


        使用FilterConfig获取Filter的初始化配置信息:

        web.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-name>MyFilter</filter-name>
               <filter-class>com.thr.MyFilter</filter-class>
               <!--配置初始化信息-->
               <init-param>
                   <param-name>name</param-name>
                   <param-value>tanghaorong</param-value>
               </init-param>
               <init-param>
                   <param-name>password</param-name>
                   <param-value>123456</param-value>
               </init-param>
           </filter>
    
           <!--映射过滤器-->
           <filter-mapping>
               <filter-name>MyFilter</filter-name>
               <!--/* :表示拦截所有的请求 -->
               <url-pattern>/*</url-pattern>
           </filter-mapping>
    
    </web-app>
    


        FilterConfig中的代码:

    package com.thr;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    import java.util.Enumeration;
    
    /**
     * @author tanghaorong
     * @date 2020-05-03
     * @desc  FilterConfig获取Filter初始化配置信息
     */
    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("Filter初始化,只初始化一次...");
    
            //获取过滤器的名字
            String filterName = filterConfig.getFilterName();
            //获取在web.xml文件中配置的初始化参数
            String initParam1 = filterConfig.getInitParameter("name");
            String initParam2 = filterConfig.getInitParameter("password");
    
            System.out.println(filterName);
            System.out.println(initParam1);
            System.out.println(initParam2);
    
            //返回过滤器的所有初始化参数的名字的枚举集合。
            Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
            while (initParameterNames.hasMoreElements()) {
                String name = (String) initParameterNames.nextElement();
                String value = filterConfig.getInitParameter(name);
                System.out.println(name + "=" + value );
            }
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    
            System.out.println("拦截前执行...");
            filterChain.doFilter(request,response);
            System.out.println("拦截后执行...");
        }
    
        @Override
        public void destroy() {
            System.out.println("Filter销毁...");
        }
    }


        使用注解获取:

    package com.thr;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    
    /**
     * @author tanghaorong
     * @date 2020-05-03
     * @desc 使用注解配置Filter初始化参数,并且用FilterConfig获取
     */
    @WebFilter(filterName = "myFilter",value = "/*",initParams = {
            @WebInitParam(name = "name", value = "tanghaorong"),/*这里配置初始化的参数*/
            @WebInitParam(name = "password", value = "123456")/*相当于<init-param>*/
    })
    public class MyFilter implements Filter {
    
        //此段代码和上面是一样的
        code...
    
    }
    
  • 相关阅读:
    斐波那契数列的量化分析
    GridView编辑删除操作
    Linux crontab 命令格式与具体样例
    VB.NET版机房收费系统---组合查询
    XMLHTTP使用具体解释
    Android 在子线程中更新UI的几种方法
    国产操作系统剽窃Linux内核可耻!
    Android的PVPlayer介绍
    稀疏矩阵
    很好的理解遗传算法的样例
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/12811457.html
Copyright © 2020-2023  润新知