filter(过滤器)和listener(监听器)

Filter

简介

Filter也称之为过滤器,是Servlet2.3新增的一个特性,主要用于过滤url请求,通过FIlter我们可以实现URL请求资源权限验证,用户登录检测等功能。

Filter是一个接口,实现一个Filter只需要重写init、doFilter、destroy方法即可,其中过滤逻辑都在doFilter方法中实现。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

Filter的工作原理

Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:

  • 调用目标资源之前,让一段代码执行。
  • 是否调用目标资源(也就是是否让用户访问web资源)
  • 调用目标资源之后,让一段代码执行。

web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。

Filter的使用

通常,我们使用filter过滤器一般是用来进行一些统一的操作,比如登录验证,对拼接了用户输入的请求进行过滤等。

定义步骤:

  • 定义一个类,视线接口Filter
  • 重写方法
  • 配置拦截路径

针对于配置拦截路径的方法有两种,第一种就是之前经常使用的在代码前面加上注解进行配置,第二钟就是在web.xml中进行配置。

注解配置拦截路径

package Fileter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;


@WebFilter("/*")
public class Demo01 implements Filter{
    public void destroy(){
        System.out.println("filter销毁");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        System.out.println("调用servlet之前执行一段代码");
        chain.doFilter(request,response);
        System.out.println("调用servlet之后执行的一段代码");
    }

    public void init(FilterConfig config) throws ServletException{

    }
}

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>Demo01</filter-name>  //声明名字
        <filter-class>Fileter.Demo01</filter-class> //指定filter过滤器
    </filter>
    
    <filter-mapping>
        <filter-name>Demo01</filter-name>
        <url-pattern>/*</url-pattern> //声明拦截对象  
    </filter-mapping>
</web-app>

在刚才的代码演示里面,可以看到filter类需要重写三个方法,分别是:

  • init:在服务器启动后,会创建Filter对象,然后调用init方法,只执行一次,用于加载资源
  • doFilter:请求的时候执行的代码主体,如果我们需要让过滤器完成什么功能,就可以将代码写在其中,该方法会在每一次请求被拦截的时候执行,可执行多次
  • destroy:在服务器关闭后,Filter对象被销毁,如果服务器是正常关闭的,就会执行destroy方法,只执行一次,用来释放资源。

除了之前演示的那种之外,拦截器还能拦截以下三种类型的请求。

  1. 具体资源路径:/test.jsp 如果这么定义,就会拦截指定的资源,也就是在用户访问这个页面的时候,过滤器才会执行。
  2. 目录拦截: /test/* 如果这么定义,就会拦截任意对指定文件夹发起的请求,也就是在用户访问这个目录下文件的时候,过滤器才会执行。
  3. 后缀名拦截: *.jsp 如果这么定义,就会拦截后缀名是.jsp的文件发起的请求,也就是在用户访问指定后悔名的时候,过滤器才会执行。

定义拦截方式

注解里面定义拦截路径,默认是REQUEST方式,也就是浏览器直接访问,使用转发或者是其他请求方式一样是会被拦截器给拦截的。

如果我们希望转发访问资源不被拦截器拦截,可以在注解中配置dispatcherTypes属性的值。

dispathcherTypes有五种属性:

  • REQUEST:浏览器直接请求资源(默认值)
  • FORWARD:转发访问资源
  • INCLUDE:包含访问资源
  • ERROR:错误跳转资源
  • ASYNC:异步访问资源

注解方式定义拦截方式

package Fileter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;


@WebFilter(value = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})// 定义浏览器请求和转发拦截器执行
public class Demo01 implements Filter{
    public void destroy(){
        System.out.println("filter销毁");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        System.out.println("调用servlet之前执行一段代码");
        chain.doFilter(request,response);
        System.out.println("调用servlet之后执行的一段代码");
    }

    public void init(FilterConfig config) throws ServletException{

    }
}

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>Demo01</filter-name>
        <filter-class>Fileter.Demo01</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>Demo01</filter-name>
        <url-pattern>/*</url-pattern>
        
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    
</web-app>

Filter链

在一个web应用中,可以开发编写多个Filter,这些个Filter组合起来称之为Filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter, 如果有,则调用第二个filter,如果没有,则调用目标资源。

Listener

简介

监听器用于监听web应用中某些对象、信息的创建、销毁、增加、修改、删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。

分类

按照监听的对象划分

  • ServletContext对象监听器
  • HttpSession对象监听器
  • ServletRequest对象监听器

按监听的事件划分

  • 对象自身的创建和销毁的监听器
  • 对象中属性的创建和消除的监听器
  • session中的某个对象的状态彼岸花的监听器

监听器的例子:用监听器统计网站在线人数

原理:每当有一个访问连接到服务器时,服务器就会创建一个session来管理会话。那么我们就可以通过统计session的数量来获得当前在线人数。

事件监听机制:

  • 事件:一件事情
  • 事件源:事件发生的地方
  • 监听器:一个对象
  • 注册监听:将事件、事件源、监听器绑定在一起。当事件源上发生某个事件后,执行监听器代码

监听器实现

要实现监听器,我们首先需要实现ServletContextListener接口,该接口没有定义好的实现类,需要我们自己去定义。

ServletContextListener方法

  • void contextDestroyed(ServletContextEvent sce):ServletContext对象被销毁之前会调用该方法
  • void contextInitialized(ServletContextEvent sce):ServletContext对象创建后会调用该方法

使用

  1. 配置web.xml

     <context-param>
            <param-name>configLocation</param-name>
            <param-value>/WEB-INF/config.xml</param-value>
        </context-param>
    
  2. 代码

    package Listener;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    @WebListener
    public class Demo01 implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent){
            ServletContext servletContext = servletContextEvent.getServletContext();
            String contextConfig = servletContext.getInitParameter("configLocation"); //获取configLocation参数
            String realPath = servletContext.getRealPath(contextConfig);//获取路径
            try {
                FileInputStream fis = new FileInputStream(realPath);//读取文件
                System.out.println(fis);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent){
            System.out.println("listener被销毁了");
        }
    }
    

原文地址:https://www.cnblogs.com/Xiaoming0/p/14147535.html