欢迎关注Hadoop、Spark、Flink、Hive、Hbase、Flume等大数据资料分享微信公共账号:iteblog_hadoop
  1. 文章总数:978
  2. 浏览总数:11,981,462
  3. 评论:3939
  4. 分类目录:106 个
  5. 注册用户数:6130
  6. 最后更新:2018年12月15日
过往记忆博客公众号iteblog_hadoop
欢迎关注微信公众号:
iteblog_hadoop
大数据技术博客公众号bigdata_ai
大数据猿:
bigdata_ai

Servlet过滤器和监听器

一、过滤器
过滤器这个名字上可以得知就是在源数据和目标数据之间起到过滤作用的中间组件。例如家里用的纯净水过滤器,将自来水过滤为纯净水。过滤器是在Servlet2.3规范中引入的新功能,并在Servlet2.4规范中得到增强。它是在服务端运行的Web组件程序,可以截取客户端给服务器发的请求,也可以截取服务器给客户端的响应。它的工作原理:当Web容器获得一个对资源的请求时,Web容器判断是否存在过滤器和这个资源关联。如果有存在关联就把请求交给过滤器去处理,在过滤器中可以对请求的内容做出改变,然后再将请求转交给被请求的资源。当被请求的资源做出响应时,Web容器同样会将响应先转发给过滤器,在过滤器中可以对响应做出处理然后再将响应发送给客户端。在这整个过程中客户端和目标资源是不知道过滤器的存在的。整个的过程如下图所示:

Servlet过滤器和监听器
Servlet过滤器和监听器

在一个Web应用程序中可以配置多个过滤器,从而形成过滤器链。在请求资源时,过滤器链中的过滤器依次对请求作出处理。在接受到响应时再按照相反的顺序对响应作出处理,整个过程如下图所示:

Servlet过滤器和监听器
Servlet过滤器和监听器

需要注意的是在过滤器中不一定必须将请求发送给被请求资源,也可以直接给客户端做出响应。
Servlet过滤器是Servlet的一种特殊的用法,主要用来完成一些通用的操作。(1)、用户认证与授权管理;(2)、统计Web应用的访问量和访问命中率从而形成访问报告;(3)、实现Web应用的日志处理功能;(4)、实现数据压缩功能;(5)、对传输的数据进行加密。
在程序中使用过滤器主要分两步:(1)、实现java.servlet.Filter的接口;(2)配置Servlet过滤器。Filter接口中主要提供了三个方法,我们可以根据需要在里面写一些过滤的处理操作。这三个方法分别如下所示:

init(FilterConfig config);

init(FilterConfig config): 用于初始化过滤器,并获取web.xml文件中配置的初始化参数。

doFilter(ServletRequest req, ServletResponse res, FilterChain chain);

用于进行过滤操作,req对象给过滤器提供了对输入的信息(表单数据、Cookie和HTTP请求头等)的完全访问;响应信息使用res对象,通常在简单的过滤器忽略此参数;chain用来调用过滤器中的下一个资源,用于访问后续过滤器。

destroy();

Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源。一个简单的过滤器可以如下所示:

package com.iteblog;

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

/**
 * Created with IntelliJ IDEA.
 * User: 过往记忆
 * Blog: https://www.iteblog.com
 * Date: 13-8-1
 * Time: 下午7:55
 */
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化了!");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器1,这里处理一些过滤操作!");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器1响应!");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器1被销毁!");
    }
}

弄完之后,这个过滤器还不能工作,需要在项目中的web.xml中做如下配置:

 <filter>
        <filter-name>MyFilter1</filter-name>
        <filter-class>com.iteblog.MyFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>MyFilter1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

好了,启动Tomcat,这个过滤器就可以工作了。下面是输出的结果:

初始化了!
过滤器1,这里处理一些过滤操作!
过滤器1响应!
过滤器1被销毁!

在标签中存在标签,我们可以在里面做一些参数配置,而init方法中的FilterConfig可以通过getInitParameter(String param)得到标签中配置好的参数,具体如下:

package com.iteblog;

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

/**
 * Created with IntelliJ IDEA.
 * User: 过往记忆
 * Blog: https://www.iteblog.com
 * Date: 13-8-1
 * Time: 下午7:55
 */
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(filterConfig.getInitParameter("test"));
        System.out.println("初始化了!");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器1,这里处理一些过滤操作!");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器1响应!");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器1被销毁!");
    }
}

web.xml配置

 <filter>
        <filter-name>MyFilter1</filter-name>
        <filter-class>com.iteblog.MyFilter</filter-class>
        <init-param>
            <param-name>test</param-name>
            <param-value>Just a test!!</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>MyFilter1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

这样就可以得到test的值,很简单吧。其实,filter也可以对一个具体的Servlet配置,如下:

    <filter-mapping>
        <filter-name>MyFilter1</filter-name>
        <servlet-name>MyServlet</servlet-name>
    </filter-mapping>
    <servlet>
         <servlet-name>MyServlet</servlet-name>
         <servlet-class>com.iteblog.MyServlet</servlet-class>
     </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>

这时,只要运行MyServlet,就可以启动MyFilter1对其进行过滤操作。细心的读者可能发现,在doFilter方法中的第三个参数FilterChain是用来干嘛的?前面不是说了在Servlet容器中存在多个过滤器,从未形成过滤器链,如果存在多个过滤器链,可以通过filterChain.doFilter(servletRequest, servletResponse);处理下一个过滤器,直到没有过滤器了才返回。假如我们项目中还存在一个过滤器:

package com.iteblog;

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

/**
 * Created with IntelliJ IDEA.
 * User: 过往记忆
 * Blog: https://www.iteblog.com/
 * Date: 13-8-1
 * Time: 下午8:12
 * To change this template use File | Settings | File Templates.
 */
public class MyFilter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器2被调用");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器2响应了");
    }

    @Override
    public void destroy() {
    }
}

同样,为了使这个过滤器在项目中起作用,也需要在web.xml中做相应的配置,如下

    <filter>
        <filter-name>MyFilter2</filter-name>
        <filter-class>com.iteblog.MyFilter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

两个过滤器运行结果如下:

过滤器1,这里处理一些过滤操作!
过滤器2被调用
过滤器2响应了
过滤器1响应!

看到了吧,调用结果是从过滤器1到过滤器2,然后从过滤器2返回到过滤器1,他们的运行过程如下图:

Servlet过滤器和监听器
Servlet过滤器和监听器

再一次把上面两个过滤器在web.xml的配置顺序列一下:

    <filter>
        <filter-name>MyFilter1</filter-name>
        <filter-class>com.iteblog.MyFilter</filter-class>
        <init-param>
            <param-name>test</param-name>
            <param-value>Just a test!!</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>MyFilter1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>MyFilter1</filter-name>
        <servlet-name>MyServlet</servlet-name>
    </filter-mapping>

    <filter>
        <filter-name>MyFilter2</filter-name>
        <filter-class>com.iteblog.MyFilter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

大家是否发现什么规则吗?是的,项目中的过滤器执行顺序和过滤器在web.xml中执行的顺序有关,其实它们执行的顺序如下:

  1. 以url-pattern方式配置的filter运行时肯定先于以servlet-name方式配置的filter
  2. 以url-partern方式配置的filter中,如果有多个与当前请求匹配,则按web.xml中filter-mapping出现的顺序来运行
  3. 对于以servlet-name方式配置的filter,如果有多个与当前请求匹配,也是按web.xml中filter-mapping出现的顺序来运行

二、监听器
所谓的监听器是指对整个WEB环境的监听,当被监听的对象发生情况时,立即调用相应的方法进行处理。Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContext, HttpSession 和 ServletRequest 这三个域对象。这里以ServletContextListener为例,来说明怎么用,代码如下所示:

package com.iteblog;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * Created with IntelliJ IDEA.
 * User: 过往记忆
 * Date: 13-8-1
 * Time: 下午8:29
 * To change this template use File | Settings | File Templates.
 */
public class MyLitener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //完成初始化工作
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        //完成销毁工作
    }
}

同样,在web.xml需要对其进行配置如下

    <listener>
        <listener-class>com.iteblog.MyLitener</listener-class>
    </listener>

这样,MyLitener就可以进行监听工作了。当Web容器关闭时,ServletContext被销毁会调用监听器的contextDestroyed方法。当Web容器启动时,ServletContext被创建会调用监听器的contextInitialized方法。
那如果同一个项目中既存在Listener,又存在Filter,他们的运行顺序是什么,和配置文件有关吗?其实Listener和Filter的加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 Filter写在 Listener的前面而会先加载 Filter。不管它们顺序怎么配置,Servlet总是先运行Listener再运行Filter!
(完)

本博客文章除特别声明,全部都是原创!
转载本文请加上:转载自过往记忆(https://www.iteblog.com/)
本文链接: 【Servlet过滤器和监听器】(https://www.iteblog.com/archives/630.html)
喜欢 (3)
分享 (0)
发表我的评论
取消评论

表情
本博客评论系统带有自动识别垃圾评论功能,请写一些有意义的评论,谢谢!