zoukankan      html  css  js  c++  java
  • Java ---Filter过滤器

         Filter可以视作是servlet的加强版,主要用作对用户的请求进行预处理,或者对返回给客户端的结果进行再次加工,是一个典型的链式处理模式。本篇简单介绍filter的基本使用方法,主要涉及以下内容:

    • Filter的背景知识
    • 使用Filter的流程
    • Filter的生命周期
    • 一个完整的实例

    一、Filter的简单介绍
         Filter在英文中是过滤器的意思,当然在此处的使用也是完美的切合了它的意思,我们使用filter的主要目的就是完成一个过滤的作用。可以在一个请求到达servlet之前,将其截取进行逻辑判断,然后决定是否放行到请求的servlet。也可以在一个response到达客户端之前,截取结果进行逻辑判断,然后决定是否允许返回给客户端。所以filter有如下几个种类:

    • 用户授权的filter:filter负责判断用户是否有权限请求该页面,给予过滤判断
    • 日志filter:截取某个用户在本网站上的所有请求,记录轨迹
    • 负责解码的filter:规定处理本次请求的解码方式

    最后需要注意的是,一个filter过滤器可以加在多个servlet控制器上,当然多个filter过滤器也是可以加在一个servlet控制器上的。由此也是可以看出来,我们使用filter往往是对一些公共的操作进行处理,例如:判断用户权限,解码本次请求等,还比如,我们的web应用中某些页面是需要用户登录后才能访问的,以往我们都是在每个servlet页面加上判断控制,导致代码冗余,有了filter,我们可以定义一个实现了filter的过滤器,让需要判断是否登录的页面都加上这么一个过滤器,可以大大降低代码的冗余程度。

    二、Filter的使用流程
         在Java中如果想要自定义一个filter过滤器的话,需要继承Javax.servlet.Filter接口,这个接口中只有三个方法:

    default void init(FilterConfig filterConfig)
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3)
    default void destroy()
    

    其中init和destroy方法是有默认实现的,也就是我们不是必须重写这两个方法,但是doFilter 这个方法是一个核心的方法,是我们必须要实现的。首先我们看init方法的作用,这个方法是用来初始化filter实例的,也就是当用户请求了某个拦截器而此拦截器又匹配了某个过滤器,此时web容器就会定位到该过滤器然后创建该filter类的实例对象并调用此实例的init方法,完成初始化工作。对于destroy方法毋庸置疑就是在过滤器执行结束的时候调用,主要完成对一些资源的释放。下面主要看dofilter这个方法。
         doFilter方法是filter接口中的核心方法,一旦创建完该过滤器的实例之后,会执行dofilter方法,所有的过滤逻辑都是在此方法中进行的。主要有三个参数,第一个参数是一个ServletRequest对象,HttpServletRequest继承于此接口,当用户请求某个拦截器的时候,检测到此请求存在过滤器,于是会封装好本次请求的相关数据,传递给dofilter的ServletRequest参数,ServletResponse参数的来源和ServletRequest是一样的,都是由客户端封装过来的。至于第三个参数,这是一个FilterChain处理链,详细的介绍等说明了web.xml中配置filter之后。
         第一步如上,创建一个继承自filter接口的类,并实现其中的三个方法。第二步是在web.xml中配置该类用于过滤哪些拦截器。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_3_1.xsd"
             version="3.1">
        <!--定义filter-->
        <filter>
            <filter-name>isLogin</filter-name>
            <filter-class>Test_f.MyFilter</filter-class>
        </filter>
    
        <!--定义filter拦截的地址-->
        <filter-mapping>
            <filter-name>isLogin</filter-name>
            <url-pattern>/a</url-pattern>
        </filter-mapping>
    
        <filter-mapping>
            <filter-name>isLogin</filter-name>
            <url-pattern>/b</url-pattern>
        </filter-mapping>
    </web-app>
    

    如上述的代码,我们需要两个操作,首先是定义一个filter,指定了该filter的name和相对应的过滤器类。然后我们可以通过filter-mapping映射过滤器和URL,此处使用了两个映射,对该过滤器指定了对路径名为/a和/b的请求进行拦截。当然这个url-pattern元素的值可以有以下三种形式,完全匹配(/a/b等),目录匹配(/* 、 /abc/等),扩展名(.a,*.b等)。
         了解了配置filter的主要操作之后,我们回去看过滤器类,我们说init方法是在首次创建filter实例的时候,用于执行初始化操作的,其中有个参数FilterConfig ,这是当前filter的配置信息,其中方法如下:

    String getFilterName();
    
    ServletContext getServletContext();
    
    String getInitParameter(String var1);
    
    Enumeration<String> getInitParameterNames();
    

    其实在创建filter实例的时候,web容器会将此实例对应在web.xml中的配置信息读取并封装成一个FilterConfig 对象传递给init方法,getFilterName方法就是我们在web.xml中配置的filter-name,getServletContext会获取当前servlet的上下文,当我们在定义filter的时候使用<init-param>来定义一些初始化参数的时候,就可以使用此方法来获取这些初始化参数。getInitParameterNames方法用于获取所有初始化参数的枚举集合。这样我们在init方法中就可以获取这些配置参数,初始化filter实例。
         上面我们只定义了一个filter,如果我们对于一次请求需要执行多个filter,进行过滤操作的话,web容器会在你请求某个URL的时候,在web.xml中找到所有匹配的filter,按照注册的顺序以FilterChain 链的形式传递到方法doFilter的第三个参数中,而这个filterChain中只有一个方法:

    void doFilter(ServletRequest var1, ServletResponse var2)
    

    如果我们在 void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) 方法中调用var3.doFilter(var1,var2),就代表此filter实例结束,则web服务器会检查FilterChain对象中是否还有filter对象(因为这是一个链,filter的数量是大于等于一的),如果没有就会放行,直接调用目标地址,如果还有filter对象,就会转而执行下一个filter。正是由于这样的机制,我们才可以对于一个URL请求添加多个filter过滤器。

    三、一个简单的实例
    下面通过一个简单的实例直观的感受下filter过滤器的作用:

    public class MyFilter implements Filter {
        @Override
        public void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException{
    
            HttpServletRequest req = (HttpServletRequest)var1;
            HttpSession session = req.getSession();
            String state = (String)session.getAttribute("state");
            if(state.equals("1")){
                var3.doFilter(var1,var2);
            }else{
                HttpServletResponse response =  (HttpServletResponse)var2;
                response.sendRedirect("Error_page.html");
            }
        }
    }
    
    //web.xml
    	<filter>
            <filter-name>isLogin</filter-name>
            <filter-class>Test_f.MyFilter</filter-class>
        </filter>
    
        <!--定义filter拦截的地址-->
        <filter-mapping>
            <filter-name>isLogin</filter-name>
            <url-pattern>/index.jsp</url-pattern>
        </filter-mapping>
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title></title>
      </head>
      <body>
        <h1>这是index页面</h1>
      </body>
    </html>
    
    //set.jsp
    //为了使程序简单,我们采用手动设置session
    //在实际的项目中,当用户登录之后自动设置session
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title></title>
      </head>
      <body>
        <%pageContext.getSession().setAttribute("state","0");%>
      </body>
    </html>
    

    我们首先访问set.jsp页面设置本次会话的session值,然后修改浏览器地址栏访问index.jsp页面
    这里写图片描述

    敲下回车键,结果如下:

    这里写图片描述

    我们先看,敲下回车键之后,程序怎么执行的,因为我们在web.xml中配置了MyFilter的拦截URL为index.jsp,所以当我们访问index.jsp的时候,会创建MyFilter 实例对象,封装配置信息到FilterConfig对象中,然后封装request请求和response,还有从web.xml 中读取的FilterChain对象传入MyFilter的doFilter方法中,我们在其中获取本次会话的session对象,取得其中的数据,如果为一放行,否则跳转到错误页面。此处的state的session值为0,我们在set.jsp中设置的,大家也可以在set.jsp页面设置其值为1,这样最终的结果会是这样的:

    这里写图片描述

    上述的demo只是为了简单的演示,其实使用filter可以完成大大的降低我们的代码的冗余程度。这个例子是为了演示,所以很简单。

    四、Filter 的生命周期
         当用户请求某个页面时候,会到web.xml中匹配是否存在能够匹配上此次请求的filter,如果有封装它的配置信息,FilterChain链。然后调用init方法,完成初始化,接着调用dofilter方法,处理核心逻辑,当此实例被销毁的时候,会调用destroy方法。

    本篇文章结束,如果有理解错误,望大家指出!

  • 相关阅读:
    【经典】仙岛求药(一)
    6月份学习记录
    YZM的全排列
    最长公共子序列的长度
    20612统计八连块
    积木城堡
    不同组合数求和
    50136142WXY的百度地图
    50095106扔核弹
    【其他】关于海岛帝国互测系列赛总结
  • 原文地址:https://www.cnblogs.com/yangming1996/p/6691840.html
Copyright © 2011-2022 走看看