Filter与Listener

本文详细介绍了Servlet中的Filter和Listener。Filter主要用于在请求到达Servlet之前和响应返回客户端之前进行拦截处理,如设置编码、权限验证等。Filter的生命周期在应用启动时开始,拦截规则由web.xml配置。Listener则监听web应用中对象、信息的创建、销毁和变化,如统计在线人数、监控session状态等。文章通过具体示例展示了Filter和Listener的实现和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Filter简介
 

ServletAPI中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。

通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。简单说,就是可以实现web容器对某资源的访问前截获进行相关的处理,还可以在某资源向web容器返回响应前进行截获进行处理。

如图,浏览器发出的请求先递交给第一个filter进行过滤,符合规则则放行,递交给filter链中的下一个过滤器进行过滤。过滤器在链中的顺序与它在web.xml中配置的顺序有关,配置在前的则位于链的前端。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉。

     在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作,chain.doFilter后面的代码一般是对response执行的操作。

二、Filter实现拦截的原理

 Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前(服务器内部对资源的访问机制决定的),都会先调用一下filter的doFilter方法。

应用举例:

1、批量设置请求编码

public class EncodingFilter implements Filter {  
  
    private String encoding = null;  
  
    public void destroy() {  
        encoding = null;  
    }  
  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
        String encoding = getEncoding();  
        if (encoding == null){  
            encoding = "gb2312";  
        }  
        request.setCharacterEncoding(encoding);// 在请求里设置上指定的编码  
        chain.doFilter(request, response);  //通过控制对chain.doFilter的方法的调用,来决定是否需要访问目标资源
    }  
  
    public void init(FilterConfig filterConfig) throws ServletException {  
        this.encoding = filterConfig.getInitParameter("encoding");  
    }  
  
    private String getEncoding() {  
        return this.encoding;  
    }  
  
}

xml配置代码


<filter>  
    <filter-name>EncodingFilter</filter-name>  
    <filter-class>com.logcd.filter.EncodingFilter</filter-class>  
    <init-param>  
       <param-name>encoding</param-name>  
       <param-value>gb2312</param-value>  
    </init-param>  
</filter>  
  
<filter-mapping>  
   <filter-name>EncodingFilter</filter-name>  
   <url-pattern>/*</url-pattern>  
</filter-mapping> 

配置规则:

在配置中需要注意的有两处:一是<filter-class>指明过滤器类所在的包路径。二是<url-pattren>处定义过滤器作用的对象。一般有以下规则:

    1:作用与所有web资源:<url—pattern>/*</url-pattern>。则客户端请求访问任意资源文件时都要经过过滤器过滤,通过则访问文件,否则拦截。

    2:作用于某一文件夹下所有文件:<url—pattern>/dir/*</url-pattern>

    3:作用于某一种类型的文件:<url—pattern>*.扩展名</url-pattern>。比如<url—pattern>*.jsp</url-pattern>过滤所有对jsp文件的访问请求。

    4:作用于某一文件夹下某一类型文件:<url—pattern>/dir/*.扩展名</url-pattern>

    如果一个过滤器需要过滤多种文件,则可以配置多个<filter-mapping>,一个mapping定义一个url-pattern来定义过滤规则。

如上的代码完成的功能为,无论进入那个页面,都要先执行EncodingFilter类的dofilter方法设置字符集

其中,doFilter()方法类似于Servlet接口的service()方法。当客户端请求目标资源的时候,容器就会调用与这个目标资源相关联的过滤器的doFilter()方法。

参数 request, response 为web 容器或 Filter 链的上一个 Filter 传递过来的请求和相应对象;参数 chain 代表当前 Filter 链的对象。

对于FilterChain接口,代表当前Filter链的对象。由容器实现,容器将其实例作为参数传入过滤器对象的doFilter()方法中。

过滤器对象使用FilterChain对象调用过滤器链中的下一个过滤器,或者目标Servlet 程序去处理,也可以直接向客户端返回响应信息,或者利用RequestDispatcher的forward()和include()方法,以及HttpServletResponse的sendRedirect()方法将请求转向到其他资源。

这个方法的请求和响应参数的类型是 ServletRequest和ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议。
 

三、Filter生命周期

和Servlet一样,Filter的创建和销毁也是由WEB服务器负责。

与Servlet区别的是

1、在应用启动的时候就进行装载Filter类而servlet是在请求时才创建(但filter与Servlet的load-on-startup配置效果相同)。

2、容器创建好Filter对象实例后,调用init()方法。接着被Web容器保存进应用级的集合容器中去了等待着,用户访问资源。

3、当用户访问的资源正好被Filter的url-pattern拦截时,容器会取出Filter类调用doFilter方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用doFilter方法(Filter对象常驻留Web容器了)。

4、当应用服务被停止或重新装载了,则会执行Filter的destroy方法,Filter对象销毁。

 
四、应用

1.登录验证

package Filter;

import Model.User;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @author zyh
 * date 2021-08-16
 */
public class LoginFilter 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("loginFilter调用");
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        User user = null;
        HttpSession session = request.getSession();
        user = (User)session.getAttribute("user");
        if (user == null){
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("<script> alert('您还没登录') </script>");
            response.setHeader("Refresh","0;url='/login.html'");
        }else {
            filterChain.doFilter(request,response);
        }
    }

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

2、设置字符编码

* Filter:过滤器 过白了就是在请求到达servlet之前,response到达servlet容器之前,拦截请求和响应,并且做一些操作

 *

 * Filter的作用:

 *      1.设置编码集 ps:一般来说只会在filter里面设置编码,不会改response的contentType

 *      2.做权限控制 -> 即没有登录的人,滚回去登录

 *      3.做一些业务逻辑 ->针对不同业务场景来做响应的业务逻辑

 *      ...

 *

 * Filter做过滤时:

 *      1.当servlet容器解析完请求报文以后,会将request对象发送给filter

 *      2.当filter做完过滤操作以后,会把request对象发送给对应的servlet

 *      3.servlet处理完请求以后,返回一个响应对象,这个response对象也是先发送给filter进行处理

 *      4.filter处理完请求以后,把response对象交给servlet容器解析为报文

 *

 * 怎么实现一个Filter:

 *      1.编写一个类 实现Filter接口

 *      2.重写init doFilter destory方法

 *      3.在doFilter方法中,将servletRequest和servletResponse强制转换成HttpServletRequest和HttpServletResponse

 *      4.在web.xml中配置相关的Filter,filter的url-partten指明是拦截哪一个servlet *表明所有的servlet

 *

 * FilterChain对象:

 *      如果程序中有很多的过滤器的话,可以调用filterChain的doFilter方法来交给下一个filter进行处理(即调用下一个filter的doFilter()方法)

        Filter的调用顺序是谁在web.xml之前定义谁先调用

        如果filterChain里面没有了filter的话,最后一个filter会把请求和响应转发给servlet

        filterChian.dofilter -> 就是方法调用 调用下一个filt

er的doFilter()方法

五、Listener

监听器:顾名思义,就是在一些事件发生的时候,对这些事情进行一些响应

 *      监听context,session和request对象创建和销毁事件以及属性改变事件(说白了就是设置在这些作用域内部的对象发生了改变)

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

    分类:

    按监听的对象划分,可以分为

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

    按监听的事件划分

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

在Servlet规范中定义了多种类型的监听器(一共8个监听器),它们用于监听的事件源分别为ServletContext,HttpSession和ServletRequest这三个域对象。Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:

1.域对象的生命周期监听:监听域对象自身的创建和销毁。这个监听器需要实现相应的监听器接口:ServletContextListener、HttpSessionListener、ServletRequestListener。

2.域对象的属性监听:监听域对象中属性的增加和删除。这个监听器需要实现的监听器接口为:ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener

3.感知监听(都与HttpSession域对象有关):监听绑定到HttpSession域中的某个JavaBean对象的状态的监听器。这个监听器需要实现的监听器接口:HttpSessionBindingListener、HttpSessionActiveationListener.
 

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

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

    所以这里用到的是HttpSessionListener

package listener;

/**
 * @author zyh
 * @version 1.0
 * @items
 * @Date:on 2021/8/17 at 10:29
 */

import Model.User;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.*;

/**  统计网站登录的在线人数:
        *  session每创建一次,证明有一个用户访问了网站(统计网站访问的在线人数)
        *  session中每有一个属性增加的时候,证明有一个用户登录了网站(统计网站登录的在线用户)
        *
        *  思路:
        *  1.在servletContext创建的时候,可以在servletContxt中设置一个count属性用来统计网站当前登录的用户数量
        *  2.设置一个session属性添加监听器,当每次session中有属性新增的时候,就从servletContext中将count取出来,+1再放回去
        *  3.在session销毁的时候,把session中设置的user属性移除
        *   4.设置一个session属性移除监听器,监听到属性移除的时候,将count从context中拿出来并-1放回去

 */
public class OnlineUserListener implements HttpSessionAttributeListener, ServletContextListener , HttpSessionListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        /**
         * 通过HttpSessionBindingEvent.getSession()获取session对象
         *
         */
        HttpSession httpSession=httpSessionBindingEvent.getSession();

        /**
         * 通过session获得当前的ServletContext对像
         */
        ServletContext servletContext = httpSession.getServletContext();

        /**
         * 设置一个count属性用来统计网站当前登录的用户数量
         */
        int count =(int) servletContext.getAttribute("count");
        /**
         * 设置一个session属性添加监听器,当每次session中有属性新增的时候,
         * 就从servletContext中将count取出来,+1再放回去
         */
        servletContext.setAttribute("count",count+1);
    }
    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
        HttpSession httpSession = httpSessionBindingEvent.getSession();
        ServletContext servletContext = httpSession.getServletContext();
        /**
         * 在session销毁的时候,把session中设置的user属性移除
         */
        int count = (int)servletContext.getAttribute("count");
        servletContext.setAttribute("count",count-1);

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("初始化");
        /**
         * 通过servletContextEvent.getServletContext()获得servletContext
         */
        ServletContext servletContext = servletContextEvent.getServletContext();
        /**
         * 在服务器启动的时候,在context中设置一个count属性来统计人数,初始值为0
         */
        servletContext.setAttribute("count",0);
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
    
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {

    }
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        HttpSession httpSession = httpSessionEvent.getSession();
        /**
         * 从session中将user拿出来,如果user为空,证明当前用户没有登陆,
         * 否则,证明当前的这个session是登陆的
         *
         */
        User user =(User) httpSession.getAttribute("user");
        if (user!=null){
            httpSession.removeAttribute("user");
        }
        
    }
}

public class onLineCount implements HttpSessionListener {

    public int count=0;//记录session的数量
    public void sessionCreated(HttpSessionEvent arg0) {//监听session的创建
        count++;
        arg0.getSession().getServletContext().setAttribute("Count", count);

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {//监听session的撤销
        count--;
        arg0.getSession().getServletContext().setAttribute("Count", count);
    }

}

在显示在线人数处通过session.getAttribute("Count")即可获取在线人数值。

   

HttpSessionListener用于监听用户session的创建和销毁,实现该接口的监听器需要实现如下两个方法:
1.sessionCreated(HttpSessionEvent se):用户与服务器的会话开始,创建时触发该方法。
2.sessionDestroyed(HttpSessionEvent se):用户与服务器的会话断开,销毁时触发该方法。
 HttpSessionAttributeListener则用于监听HttpSession(session)范围内属性的变化,实现该接口的监听器需要实现attributeAdded(),attributeRemoved()
由此可见,HttpSessionAttributeListener监听session范围内属性的改变,

ServletContextAttributeListener监听的是application范围内属性的改变。
实HttpSessionListener接口的监听器可以监听每个用户会话的开始断开,因此应用可以通过该监听器监听系统的在线用户。

属性范围
age:只在一个页面中保存属性,跳转之后无效
Page范围内的对象在客户端每次请求JSP时被创建,在页面向客户端发送回应或者被forward转发时被删除。
request:只在一次请求中保存,服务器跳转之后依然有效
常用的两个方法签名如下:
ublic String getParameter(String name)
ublic String[] getParameterValues(String name)
通过forward跳转后由于相当于在一次请求中,所以信息不会被删除,但是如果通过redirect方式跳转,则相当于一个新的请求,会被删除重新创建请求。
session:在一次回话范围中,无论何种跳转都可以使用,但是新开的浏览器不能使用
作用范围是在一次用户与服务器的链接时间内,如果与服务器断开链接则删除
application:在整个服务器上保存,所有的用户都可以使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值