JavaWeb学习——监听器和过滤器

监听器 与 Filters


一、概述

监听器是用于监听Web应用而实现了特定接口的Java类。监听器可以在事件发生前、后做一些有必要的处理。

Servlet API提供了一系列的事件和事件监听接口。上层的servlet/JSP应用能够通过调用这些API进行事件驱动的开发。

这里监听的所有事件都继承自java.util.EventObject对象。

 Servlet 中的监听器分类:

  • 监听三个域对象(ServletContext、HttpSession、ServletRequest)的创建和销毁的三个监听器。
  • 监听三个域对象的属性变更(添加、移除、替换)的三个监听器。
  • 监听HttpSession中JavaBean的状态变换的监听(两个)。

Filters是拦截Request请求的对象: 在用户的请求访问资源前处理ServletRequest以及ServletResponse,它可以用于日志记录、解加密、Session检查、图像文件保护等。

二、监听器

2.1 监听器接口和注册

监听器接口主要在 javax.servlet 和 javax.servlet.http 的包中。对应上文的三类,有以下这些接口:

第一类:

名称 作用
javax.servlet.ServletContextListener    它能响应Servlet生命周期事件,它提供了ServletContext创建之后和ServletContext关闭之前的会被调用的方法。  
javax.servlet.http.HttpSessionListener 它能响应HttpSession的创建、超时和失效事件。
javax.servlet.http.HttpActivationListener   它在一个HttpSession激活或者失效时被调用。
javax.servlet.ServletRequestListener 它能响应一个ServletRequest的创建或删除。

第二类:

名称 作用
javax.servlet.ServletContextAttributeListener   它能响应ServletContext范围内的属性添加、删除、替换事件。 
javax.servlet.http.HttpSessionAttributeListener  它能响应HttpSession范围内的属性添加、删除、替换事件。
javax.servlet.http.HttpSessionBindingListener 可以实现这个接口来保存HttpSession范围的属性。当有属性从HttpSession添加或者删除时,它能做出响应。 
javax.servlet.ServletRequestListener 它能响应ServletRequest范围的属性添加、删除、修改事件。

 编写一个监听器,只需要写一个Java类来实现对应的监听器接口就可以了。在Servlet 3.0和Servlet 3.1中提供了两种注册监听器的方法。一种是使用WebListener注解。例如:

@WebListener
public class ListenerClass implements ListenerInterface {
}

第二种是在部署描述文档中增加一个listener元素。

<listener>
    <listener-class>fully-qualified listener class</listener-class>
</listener>

2.2 ServletContext 监听器

2.2.1 ServletContextListener

当ServletContext 初始化时,容器会调用所有注册的ServletContextListeners 的 contextInitialized方法。

当ServletContext 将要销毁是,容器会调用所有注册的ServletContextListeners 的 contextDestroyed方法。

void contextInitialized(ServletContextEvent event)

void contextDestroyed(ServletContextEvent event)

contextInitialized方法和contextDestroyed方法都会从容器获取到一个ServletContextEvent。

javax.servlet.ServletContextEvent 是一个 java.util.EventObject的子类,它定义了一个访问ServletContext的getServletContext方法。

2.2.2 ServletContextAttributeListener

当一个ServletContext范围的属性被添加、删除或者替换时,ServletContextAttributeListener接口的实现类会接受到消息。这个接口定义了三个方法:

void attributeAdded(ServletContextAttributeEvent event)

void attributeRemoved(ServletContextAttributeEvent event)

void attributeReplaced(ServletContextAttributeEvent event)

这三个方法都能获取到一个 ServletContextAttributeEvent 的对象,通过这个对象可以获取属性的名称和值。

2.3 Session Listeners

2.3.1 HttpSessionListener

当一个HttpSession创建或者销毁时,容器都会通知所有的HttpSessionListener监听器,HttpSessionListener接口有两个方法:sessionCreated 和 sessionDestroyed。

void sessionCreated(HttpSessionEvent event)

void sessionDestroyed(HttpSessionEvent event)

这两个方法都可以接收到一个继承于 java.util.EventObject 的HttpSessionEvent 对象。可以通过调用HttpSessionEvent 对象的getSession方法来获取当前的HttpSession。

下面举一个例子,这个监听器可以用来统计HttpSession的数量。

package app08a.listener;

import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionListener implements HttpSessionListener, ServletContextListener{

    @Override
    public void contextInitialized(ServletContextEvent e){
        ServletContext servletContext = e.getServletContext();
        servletContext.setAttribute("userCount", new AtomicInteger());
    }

    @Override
    public void contextDestroyed(ServletContextEvent e){
    }

    @Override
    public void sessionCreated(HttpSessionEvent e){
        HttpSession session = e.getSession();
        ServletContext servletContext = session.getServletContext();
        AtomicInteger userCounter = (AtomicInteger)servletContext.getAttribute("userCount");
        int userCount = userCounter.incrementAndGet();
        System.out.println("userCount incremented to :"+userCount);
    }

    public void sessionDestroyed(HttpSessionEvent e){
        HttpSession session = e.getSession();
        ServletContext servletContext = session.getServletContext();
        AtomicInteger userCounter = (AtomicInteger)servletContext.getAttribute("userCount");
        int userCount = userCounter.decrementAndGet();
        System.out.println("---------- userCount decremented to :"+userCount);
    }
}

当第一次访问页面时,控制台打印如下信息:

 2.3.2 HttpSessionAttributeListener

HttpSessionAttributeListener接口有以下方法:

void attributeAdded(HttpSessionBindingEvent event)

void attributeRemoved(HttpSessionBindingEvent event)

void attributeReplaced(HttpSessionBindingEvent event)

这三个方法都能获取到一个 HttpSessionBindingEvent的对象,通过这个对象可以获取属性的名称和值。

由于HttpSessionBinding是 HttpSessionEvent的子类,因此也可以在HttpSessionAttributeListener实现类中获得HttpSession。

2.3.3 HttpSessionActivationListener

在分布式环境下,会用多个容器进行负载均衡,有可能需要将session保存起来,在容器之间传递。例如当一个容器内存不足时,会把很少用到的对象转存到其他容器上,这时候,容器就会通知所有HttpSessionActivationListener接口实现的类。

HttpSessionActivationListener接口有两个方法,sessionDidActivate 和 sessionWillPassivate。

void sessionDidActivate(HttpSessionEvent event)

void sessionWillPassivate(HttpSessionEvent event)

当HttpSession被转移到其他容器之后,sessionDidActivate就会被调用。容器将一个HttpSession方法传递到方法里,可以从这个对象获得HttpSession。

2.3.4 HttpSessionBindingListener

当有属性绑定或者解绑到HttpSession上时,HttpSessionBindingListener 监听器就会被调用。

2.4 ServletRequest Listeners

2.4.1 ServletRequestListener

ServletRequestListener监听器会对ServletRequest的创建和销毁事件进行响应。容器通过一个池来存放并重复利用多个ServletRequest,ServletRequest的创建是从容器池里被分配出来的时刻开始,而它的销毁时刻是放回池里的事件。

ServletRequestListener接口有两个方法:

void requestInitialized(ServletRequestEvent event)

void requestDestroyed(ServletRequestEvent event)

这两个方法都会接收到一个ServletRequestEvent对象,可以通过这个对象的getServletRequest方法来获取ServletRequest对象。

另外,ServletRequestEvent接口也提供了一个getServletContext方法来获取ServletContext。

2.4.2 ServletRequestAttributeListener

此接口提供了三个方法:

void attributeAdded(ServletRequestAttributeEvent event)

void attributeRemoved(ServletRequestAttributeEvent event)

void attributeReplaced(ServletRequestAttributeEvent event)

三、Filters

3.1 Filter API

下面介绍与Filter相关的接口,包含 Filter、FilterConfig、Filter Chain。

Filter的实现必须继承 javax.servlet.Filter 接口。这个接口包含了 Filter 的三个生命周期:init,doFilter,destroy。

 Servlet容器初始化 Filter 时,会触发 Filter 的 init 方法,一般在应用开始时,而不是在相关资源使用时才初始化,这个方法只调用一次。init 定义如下:

void init(FilterConfig filterConfig)

当Servlet 容器每次处理Filter相关资源时,都会调用该Filter实例的doFilter方法。doFilter包含三个参数,定义如下:

void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

在doFilter中可以访问ServletRequest、ServletResponse。

在Filter的doFilter的实现中,最后一行需要调用FilterChain 中的 doFilter 方法。注意Filter的doFilter方法里的第三个参数,就是filterChain的实例:

filterChain.doFilter(request, response)

一个资源可能被多个Filter关联到(即Filter 链条),这时Filter.doFilter()的方法将会触发Filter链条中的下一个Filter。只有在Filter链条中最后一个Filter里调用的FilterChain.doFilter(),才会触发处理资源的方法。

 如果在Filter,doFilter()的实现中,没有在结尾处调用FilterChain.doFilter() 的方法,那么该Request请求终止,后面的处理就会中断。

注意:FilterChain接口中,唯一的方法是doFilter。该方法与Filter中的doFilter方法的定义是不一样的,它只有两个参数。

Filter接口中,最后一个方法是destroy,定义如下:

void destroy()

该方法在Servlet容器要销毁Filter时触发,一般在应用停止的时候进行调用。

3.2 Filter配置

当完成Filter的实现后,就要进行配置Filter了。步骤如下:

  •  确认哪些资源需要使用这个Filter拦截处理。
  •  配置Filter的初始化参数值,这些参数可以在Filter的init方法中读取到:
  •  给Filter取一个名称。一般来说,这个名称没什么特别含义。特殊的情况,例如要记录Filter的初始化时间,但这个应用中有许多的Filter,这时它就可以用来识别Filter了。

FilterConfig接口允许通过它的getServletContext的方法来访问ServletContext:

ServletContext  getServletContext()

如果配置了 Filter 的名字,在FilterConfig 的getFilterName 中就可以获取Filter的名字。定义如下:

java.lang.String getFilterName()

 当然,最重要还是要获取配置给Filter的初始化参数,需要用到FilterConfig中的两个方法,第一个方法是getParameterNames:

java.util.Enumeration<java.lang.String> getInitParametersNames()

 这个方法返回Filter参数名字的Enumeration对象。如果没有给这个Filter配置任何参数,返回空。

 第二个方法是getParameter:

java.lang.String getInitParameter(java.lang.String parameterName)

有两种方法可以配置Filter:一种是通过WebFilter的Annotation来配置Filter,另一种是通过部署描述来注册。下面是WebFilter的参数,所有参数都是可选的。

属性 描述
asyncSupported     Filter是否支持异步操作  
description Filter的描述
dispatcerTypes Filter所生效范围
displayName Filter的显示名
filterName Filter的名称
initParams Filter的初始化参数
largeIcon Filter的大图名称
servletName Filter所生效的Servlet名称  
smallIcon Filter的小图名称
urlPatterns Filter所生效的URL路径
value   Filter所生效的URL路径

 下面举个例子:

@WebFilter(filterName = "Security Filter", urlPatterns = {"/*"},
                initParams = {
                    @WebInitParam(name = "frequency", value = "1909"),
                    @WebInitParam(name = "resolution", value = "1024")
                }
}

 如果使用部署描述符,那么对应的配置为:

<filter>
    <filter-name>Security Filter</filter-name>
    <filter-class>filterClass</filter-class>
    <init-param>
        <param-name>frequency</param-name>
        <param-value>1909</param-value>
    </init-param>
    <init-param>
        <param-name>resolution</param-name>
        <param-value>1024</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>DateCompressionFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3.3 示例1:日志Filter

package filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "LoggingFilter", urlPatterns= {"/*"},
        initParams = {
                @WebInitParam(name = "logFileName", value = "log.txt"),
                @WebInitParam(name = "prefix", value="URI: ")
        })
public class LoggingFilter implements Filter{

    private PrintWriter logger;
    private String prefix;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException{
        prefix = filterConfig.getInitParameter("prefix");
        String logFileName = filterConfig.getInitParameter("logFileName");
        String appPath = filterConfig.getServletContext().getRealPath("/");

        System.out.println("logFileName:" + logFileName);
        try{
            logger = new PrintWriter(new File(appPath, logFileName));
        }catch(FileNotFoundException e){
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }

    @Override
    public void destroy(){
        System.out.println("destroying filter");
        if(logger != null)
            logger.close();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws IOException, ServletException{
        System.out.println("LoggingFilter.doFilter");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        logger.println(new Date() + "" + prefix + httpServletRequest.getRequestURL());
        logger.flush();
        filterChain.doFilter(request, response);
    }
}

 通过检查日志文件的内容,就可以验证这个Filter是否运行正常。

3.4 示例2:图像文件保护Filter

 实现检查HTTP Header的referrer值,只有通过页面访问获取图片资源,直接输入文件路径无效。

package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "ImageProtetorFilter", urlPatterns = {"*.png","*.jpg","*.gif"})
public class ImageProtectorFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException{
    }

    @Override
    public void destroy(){}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws IOException, ServletException{
        System.out.println("ImageProtectorFilter");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String referrer = httpServletRequest.getHeader("referer");
        System.out.println("referrer:" + referrer);
        if(referrer!=null)
            filterChain.doFilter(request, response);
        else
            throw new ServletException("Image not available");
    }
}

3.5 示例3:下载计数Filter

package filter;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "DownloadCounterFilter", url = {"/*"})
public class DownLoadCounterFilter implements Filter{

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Properties downloadLog;
    File logFile;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException{
        System.out.println("DownloadCounterFilter");
        String appPath = filterConfig.getServletContext().getRealPath("/");
        logFile = new File(appPath, "downloadLog.txt");
        if(!logFile.exists()){
            try{
                logFile.createNewFile();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
        downloadLog = new Properties();
        try{
            downloadLog.load(new FileReader(logFile));
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public void destroy(){}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException{
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        final String uri = httpServletRequest.getRequestURI();
        executorService.execute(new Runnable(){
            @Override
            public void run(){
                String property = downloadLog.getProperty(uri);
                if(property == null){
                    downloadLog.setProperty(uri, "1");
                }else{
                    int count = 0;
                    try{
                        count = Integer.parseInt(property);
                    }catch(NumberFormatException e){

                    }
                    count++;
                    downloadLog.setProperty(uri, Integer.toString(count));
                }
                try{
                    downloadLog.store(new FileWriter(logFile),"");
                }catch(IOException e){}
            }
        });
        filterChain.doFilter(request, response);
    }
}

3.6 Filter顺序

 如果多个Filter应用于同一个资源,Filter的触发顺序将会很重要,需要使用部署描述符来管理Filter。举个例子,Filter1要在Filter2前被触发:

<filter>
    <filter-name>Filter1</filter-name>
    <filter-class>the fully-qualified name1</filter-class>
</filter>
<filter>
    <filter-name>Filter2</filter-name>
    <filter-class>the fully-qualified name2</filter-class>
</filter>

如果需要保持或者改变Filter实现中的状态,就要考虑到线程安全问题。

<------   天若有情天亦老,人间正道是沧桑   ------>

原文地址:https://www.cnblogs.com/cardiolith/p/9671211.html

时间: 2024-10-08 07:01:02

JavaWeb学习——监听器和过滤器的相关文章

J2EE监听器和过滤器基础

Servlet程序由Servlet,Filter和Listener组成,其中监听器用来监听Servlet容器上下文. 监听器通常分三类:基于Servlet上下文的ServletContex监听,基于会话的HttpSession监听和基于请求的ServletRequest监听. ServletContex监听器 ServletContex又叫application,存在范围是整个Servlet容器生命周期,当系统启动时就会创建,系统关闭时会销毁,该对象通常存放一些非常通用的数据,但是不推荐存放太多

JavaWeb监听器和过滤器

JavaWeb监听器 ServletContextListener监听器 Servlet的上下文监听,它主要实现监听ServletContext的创建和销毁.可以完成自己想要的初始化工作.主要包括下面两个方法 //启动服务的时候调用该方法 public void contextInitialized(ServletContextEvent sce); //销毁该服务的时候调用该方法 public void contextDestroyed(ServletContextEvent sce); Se

监听器和过滤器

过滤器生命周期的四个阶段: 1.实例化:Web容器在部署Web应用程序时对所有过滤器进行实例化.Web容器回调它的无参构造方法.2.初始化:实例化完成之后,马上进行初始化工作.Web容器回调init()方法. 3.过滤:请求路径匹配过滤器的URL映射时.Web容器回调doFilter()方法--主要的工作方法. 4.销毁: Web容器在卸载Web应用程序前,Web容器回调destroy()方法. 监听器概述 监听你的web应用,监听许多信息的初始化,销毁,增加,修改,删除值等 Servlet监听

Servlet初始配置 监听器和过滤器

ServletContext:application范围内的参数 此所设定的参 来源: http://note.sdo.com/my 数,在JSP网页中可以使用下列方法来取得: ${initParam.param0}, <%=application.getInitParameter("param0") %><br/> 若在Servlet可以使用下列方法来获得: String param_name=getServletContext().getInitParame

java web监听器和过滤器

2019-3-26 监听器:6+2 1.j监听器种类 2.监听器的编写步骤(重点): a.编写一个监听器类去实现监听器接口 b.覆盖监听器的方法 c.需要在web.xml中进行配置---注册 过滤器 Filter 过滤器的编写步骤(重点): a.编写一个过滤器类去实现过滤器接口 b.覆盖过滤器的方法 c.需要在web.xml中进行配置---注册 原文地址:https://www.cnblogs.com/houchen/p/10604782.html

Spring监听器和过滤器的全局配置

<!-- 监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>cl

JavaWeb学习篇之----Servlet过滤器Filter和监听器

JavaWeb学习篇之----Servlet过滤器Filter和监听器 首先来看一下Servlet的过滤器内容: 一.Servlet过滤器的概念: *************************************************************************************** Servlet过滤器是在Java Servlet规范2.3中定义的,它能够对Servlet容器的请求和响应对象进行检查和修改. Servlet过滤器本身并不产生请求和响应对象,

vue.js基础知识篇(1):简介、数据绑定、指令、计算属性、表单控件绑定和过滤器

目录第一章:vue.js是什么? 代码链接: http://pan.baidu.com/s/1qXCfzRI 密码: 5j79 第一章:vue.js是什么? 1.vue.js是MVVM框架 MVVM的代表框架是Angular.js,以及vue.js. MVVM的view和model是分离的,View的变化会自动更新到ViewModel上,ViewModel的变化会自动同步到View上显示.这种自动同步依赖于ViewModel的属性实现了Observer. 2.它与angular.js的区别 相同

Scala中For表达式的生成器、定义和过滤器

学习了Scala中For表达式的生成器.定义和过滤器 ,应用 for是循环列表,根据用户需要进行过滤. Def main(args:Array[String]){ Val  lauren=Person(“Lauren”,false) Val  rocky=Person(“Rocky”,true) Val  vivian=Person(“Vivian”,false, laure,rocky) Val  person=List(Lauren,Rocky,Vivian) Val forResult=f