Web-listener与filter

来源:互联网 发布:怎么用记事本编程 编辑:程序博客网 时间:2024/06/01 09:43

1、Listener的定义与作用

  监听器Listener就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。

  Listener是Servlet的监听器,可以监听客户端的请求,服务端的操作等。

2、Listener的分类与使用

  主要有以下三类:

  1、ServletContext监听

  ServletContextListener:用于对Servlet整个上下文进行监听(创建、销毁)。

public void contextInitialized(ServletContextEvent sce);//上下文初始化
public void contextDestroyed(ServletContextEvent sce);//上下文销毁
 
public ServletContext getServletContext();//ServletContextEvent事件:取得一个ServletContext(application)对象

  ServletContextAttributeListener:对Servlet上下文属性的监听(增删改属性)。

public void attributeAdded(ServletContextAttributeEvent scab);//增加属性
public void attributeRemoved(ServletContextAttributeEvent scab);//属性删除
public void attributeRepalced(ServletContextAttributeEvent scab);//属性替换(第二次设置同一属性)
 
//ServletContextAttributeEvent事件:能取得设置属性的名称与内容
public String getName();//得到属性名称
public Object getValue();//取得属性的值

  2、Session监听

  Session属于http协议下的内容,接口位于javax.servlet.http.*包下。

  HttpSessionListener接口:对Session的整体状态的监听。

public void sessionCreated(HttpSessionEvent se);//session创建
public void sessionDestroyed(HttpSessionEvent se);//session销毁
 
//HttpSessionEvent事件:
public HttpSession getSession();//取得当前操作的session

  HttpSessionAttributeListener接口:对session的属性监听。

public void attributeAdded(HttpSessionBindingEvent se);//增加属性
public void attributeRemoved(HttpSessionBindingEvent se);//删除属性
public void attributeReplaced(HttpSessionBindingEvent se);//替换属性
 
//HttpSessionBindingEvent事件:
public String getName();//取得属性的名称
public Object getValue();//取得属性的值
public HttpSession getSession();//取得当前的session

  session的销毁有两种情况:

  1、session超时,web.xml配置:

<session-config>
    <session-timeout>120</session-timeout><!--session120分钟后超时销毁-->
</session-config>

  2、手工使session失效

public void invalidate();//使session失效方法。session.invalidate();

  3、Request监听

  ServletRequestListener:用于对Request请求进行监听(创建、销毁)。

public void requestInitialized(ServletRequestEvent sre);//request初始化
public void requestDestroyed(ServletRequestEvent sre);//request销毁
 
//ServletRequestEvent事件:
public ServletRequest getServletRequest();//取得一个ServletRequest对象
public ServletContext getServletContext();//取得一个ServletContext(application)对象

  ServletRequestAttributeListener:对Request属性的监听(增删改属性)。

public void attributeAdded(ServletRequestAttributeEvent srae);//增加属性
public void attributeRemoved(ServletRequestAttributeEvent srae);//属性删除
public void attributeReplaced(ServletRequestAttributeEvent srae);//属性替换(第二次设置同一属性)
 
//ServletRequestAttributeEvent事件:能取得设置属性的名称与内容
public String getName();//得到属性名称
public Object getValue();//取得属性的值

  4、在web.xml中配置

Listener配置信息必须在FilterServlet配置之前,Listener的初始化(ServletContentListener初始化)比ServletFilter都优先,而销毁比ServletFilter都慢。

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

3、Listener应用实例

  1、利用HttpSessionListener统计最多在线用户人数

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
 
public class HttpSessionListenerImpl implements HttpSessionListener {
 
    public void sessionCreated(HttpSessionEvent event) {
        ServletContext app = event.getSession().getServletContext();
        int count = Integer.parseInt(app.getAttribute("onLineCount").toString());
        count++;
        app.setAttribute("onLineCount", count);
        int maxOnLineCount = Integer.parseInt(app.getAttribute("maxOnLineCount").toString());
        if (count > maxOnLineCount) {
            //记录最多人数是多少
            app.setAttribute("maxOnLineCount", count);
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //记录在那个时刻达到上限
            app.setAttribute("date", df.format(new Date()));
        }
    }
    //session注销、超时时候调用,停止tomcat不会调用
    public void sessionDestroyed(HttpSessionEvent event) {
        ServletContext app = event.getSession().getServletContext();
        int count = Integer.parseInt(app.getAttribute("onLineCount").toString());
        count--;
        app.setAttribute("onLineCount", count);    
        
    }
}

  2、Spring使用ContextLoaderListener加载ApplicationContext配置信息

ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。

  ContextLoaderListener如何查找ApplicationContext.xml的配置位置以及配置多个xml:如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml",在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml(在MyEclipse中把xml文件放置在src目录下)。如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext-*.xml</param-value><!-- 采用的是通配符方式,查找WEB-INF/spring目录下xml文件。如有多个xml文件,以“,”分隔。 -->
</context-param>
 
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

  3、Spring使用Log4jConfigListener配置Log4j日志

  Spring使用Log4jConfigListener的好处:

1.   动态的改变记录级别和策略,不需要重启Web应用。

2.   log文件定在 /WEB-INF/logs/而不需要写绝对路径。因为系统把web目录的路径压入一个叫webapp.root的系统变量。这样写log文件路径时不用写绝对路径了。

3.   可以把log4j.properties和其他properties一起放在/WEB-INF/,而不是Class-Path

4.   设置log4jRefreshInterval时间,开一条watchdog线程每隔段时间扫描一下配置文件的变化。

<context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>project.root</param-value><!-- 用于定位log文件输出位置在web应用根目录下,log4j配置文件中写输出位置:log4j.appender.FILE.File=${project.root}/logs/project.log -->
</context-param>
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value><!-- 载入log4j配置文件 -->
</context-param>
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value><!--Spring刷新Log4j配置文件的间隔60秒,单位为millisecond-->
</context-param>
 
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

  4、Spring使用IntrospectorCleanupListener清理缓存

  这个监听器的作用是在web应用关闭时刷新JDK的JavaBeans的Introspector缓存,以确保Web应用程序的类加载器以及其加载的类正确的释放资源。

  如果JavaBeans的Introspector已被用来分析应用程序类,系统级的Introspector缓存将持有这些类的一个硬引用。因此,这些类和Web应用程序的类加载器在Web应用程序关闭时将不会被垃圾收集器回收!而IntrospectorCleanupListener则会对其进行适当的清理,已使其能够被垃圾收集器回收。

  唯一能够清理Introspector的方法是刷新整个Introspector缓存,没有其他办法来确切指定应用程序所引用的类。这将删除所有其他应用程序在服务器的缓存的Introspector结果。

  在使用Spring内部的bean机制时,不需要使用此监听器,因为Spring自己的introspection results cache将会立即刷新被分析过的JavaBeans Introspector cache,而仅仅会在应用程序自己的ClassLoader里面持有一个cache。虽然Spring本身不产生泄漏,注意,即使在Spring框架的类本身驻留在一个“共同”类加载器(如系统的ClassLoader)的情况下,也仍然应该使用使用IntrospectorCleanupListener。在这种情况下,这个IntrospectorCleanupListener将会妥善清理Spring的introspection cache。

  应用程序类,几乎不需要直接使用JavaBeans Introspector,所以,通常都不是Introspectorresource造成内存泄露。相反,许多库和框架,不清理Introspector,例如: Struts和Quartz。

  需要注意的是一个简单Introspector泄漏将会导致整个Web应用程序的类加载器不会被回收!这样做的结果,将会是在web应用程序关闭时,该应用程序所有的静态类资源(比如:单实例对象)都没有得到释放。而导致内存泄露的根本原因其实并不是这些未被回收的类!

  注意:IntrospectorCleanupListener应该注册为web.xml中的第一个Listener,在任何其他Listener之前注册,比如在Spring's ContextLoaderListener注册之前,才能确保IntrospectorCleanupListener在Web应用的生命周期适当时机生效。

<!-- memory clean -->
<listener>
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>

 

 

Filter简介

Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter功能

·        HttpServletRequest到达 Servlet 之前,拦截客户的 HttpServletRequest。根据需要检查 HttpServletRequest,也可以修改HttpServletRequest头和数据。

·        HttpServletResponse到达客户端之前,拦截HttpServletResponse。根据需要检查 HttpServletResponse,也可以修改HttpServletResponse头和数据。

如何借助Filter实现拦截功能

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

·        调用目标资源之前,让一段代码执行。

·        是否调用目标资源(即是否让用户访问web资源)。

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

Filter开发两步走

1.    编写java类实现Filter接口,并实现其doFilter方法。

2.     web.xml 文件中使用和元素对编写的filter类进行注册,并设置它所能拦截的资源。

web.xml配置各节点介绍:

·        <filter>指定一个过滤器。

o   <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。

o   <filter-class>元素用于指定过滤器的完整的限定类名。

o   <init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。

o   在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。

·        <filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径

o   <filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字

o   <url-pattern>设置filter所拦截的请求路径(过滤器关联的URL样式)

·        <servlet-name>指定过滤器所拦截的Servlet名称。

·        <dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARDERROR之一,默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定Filter对资源的多种调用方式进行拦截。

·        <dispatcher>子元素可以设置的值及其意义

o   REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcherinclude()forward()方法访问时,那么该过滤器就不会被调用。

o   INCLUDE:如果目标资源是通过RequestDispatcherinclude()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。

o   FORWARD:如果目标资源是通过RequestDispatcherforward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。

o   ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。

Filter

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

web服务器根据Filterweb.xml文件中的注册顺序,决定先调用哪个Filter,当第一个FilterdoFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2filter,如果没有,则调用目标资源。

Filter的生命周期

public void init(FilterConfig filterConfig)throws ServletException;//初始化

和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。web应用程序启动时,web服务器将创建Filter的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) throws IOException,ServletException;//拦截请求


这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。

publicvoid destroy();//销毁


Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

FilterConfig接口

用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得以下内容:

StringgetFilterName();//得到filter的名称。

StringgetInitParameter(String name);//返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.

EnumerationgetInitParameterNames();//返回过滤器的所有初始化参数的名字的枚举集合。

publicServletContext getServletContext();//返回Servlet上下文对象的引用。


Filter使用案例

使用Filter验证用户登录安全控制

前段时间参与维护一个项目,用户退出系统后,再去地址栏访问历史,根据url,仍然能够进入系统响应页面。我去检查一下发现对请求未进行过滤验证用户登录。添加一个filter搞定问题!

先在web.xml配置

web.xml

<filter>

   <filter-name>SessionFilter</filter-name>

   <filter-class>com.action.login.SessionFilter</filter-class>

    <init-param>

       <param-name>logonStrings</param-name><!-- 对登录页面不进行过滤 -->

       <param-value>/project/index.jsp;login.do</param-value>

    </init-param>

    <init-param>

       <param-name>includeStrings</param-name><!-- 只对指定过滤参数后缀进行过滤 -->

        <param-value>.do;.jsp</param-value>

    </init-param>

    <init-param>

       <param-name>redirectPath</param-name><!-- 未通过跳转到登录界面 -->

       <param-value>/index.jsp</param-value>

    </init-param>

    <init-param>

       <param-name>disabletestfilter</param-name><!-- Y:过滤无效 -->

       <param-value>N</param-value>

    </init-param>

</filter>

<filter-mapping>

   <filter-name>SessionFilter</filter-name>

    <url-pattern>/*</url-pattern>

</filter-mapping>


接着编写FilterServlet

FilterServlet.java

packagecom.action.login;

 

importjava.io.IOException;

 

importjavax.servlet.Filter;

importjavax.servlet.FilterChain;

importjavax.servlet.FilterConfig;

importjavax.servlet.ServletException;

import javax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importjavax.servlet.http.HttpServletResponseWrapper;

 

/**

 *    判断用户是否登录,未登录则退出系统

 */

public classSessionFilter implements Filter {

 

    public FilterConfig config;

 

    public void destroy() {

        this.config = null;

    }

 

    public static boolean isContains(Stringcontainer, String[] regx) {

        boolean result = false;

 

        for (int i = 0; i < regx.length;i++) {

            if (container.indexOf(regx[i]) !=-1) {

                return true;

            }

        }

        return result;

    }

 

    public void doFilter(ServletRequestrequest, ServletResponse response, FilterChain chain) throws IOException,ServletException {

        HttpServletRequest hrequest =(HttpServletRequest)request;

        HttpServletResponseWrapper wrapper =new HttpServletResponseWrapper((HttpServletResponse) response);

 

        String logonStrings =config.getInitParameter("logonStrings");        // 登录登陆页面

        String includeStrings =config.getInitParameter("includeStrings");    // 过滤资源后缀参数

        String redirectPath =hrequest.getContextPath() + config.getInitParameter("redirectPath");//没有登陆转向页面

        String disabletestfilter =config.getInitParameter("disabletestfilter");// 过滤器是否有效

 

        if(disabletestfilter.toUpperCase().equals("Y")) {    // 过滤无效

            chain.doFilter(request, response);

            return;

        }

        String[] logonList =logonStrings.split(";");

        String[] includeList =includeStrings.split(";");

 

        if(!this.isContains(hrequest.getRequestURI(), includeList)) {// 只对指定过滤参数后缀进行过滤

            chain.doFilter(request, response);

            return;

        }

 

        if(this.isContains(hrequest.getRequestURI(), logonList)) {// 对登录页面不进行过滤

            chain.doFilter(request, response);

            return;

        }

 

        String user = ( String )hrequest.getSession().getAttribute("useronly");//判断用户是否登录

        if (user == null) {

            wrapper.sendRedirect(redirectPath);

            return;

        }else {

            chain.doFilter(request, response);

            return;

        }

    }

 

    public void init(FilterConfig filterConfig)throws ServletException {

        config = filterConfig;

    }

}


这样既可完成对用户所有请求,均要经过这个Filter进行验证用户登录。

防止中文乱码过滤器

项目使用spring框架时。当前台JSP页面和JAVA代码中使用了不同的字符集进行编码的时候就会出现表单提交的数据或者上传/下载中文名称文件出现乱码的问题,那就可以使用这个过滤器。

web.xml

<filter>

   <filter-name>encoding</filter-name>

   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

   <init-param>

       <param-name>encoding</param-name><!--用来指定一个具体的字符集-->

       <param-value>UTF-8</param-value>

   </init-param>

   <init-param>

       <param-name>forceEncoding</param-name><!--true:无论request是否指定了字符集,都是用encoding;false:如果request已指定一个字符集,则不使用encoding-->

       <param-value>false</param-value>

   </init-param>

</filter>

<filter-mapping>

   <filter-name>encoding</filter-name>

   <url-pattern>/*</url-pattern>

</filter-mapping>

 


0 0
原创粉丝点击