JavaWeb_19 Filter与Listener


 

一、Filter

概述:

          过滤器实际上就是对web资源(HTML、CSS、Servlet、JSP)进行拦截,做一些处理后再交给下一个过滤器或servlet处理
通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理

快速入门:

  • 编写一个Filter,定义类实现接口Filter,实现接口中的抽象方法
  • 在web.xml文件中配置访问过滤路径(和Servlet的配置有些相似)

Filter生命周期

  • 构造器:Filter的构造器在服务器启动时调用。 构造器只会调用一次,说明Filter也是单例多线程的。
  • init():在构造器被调用后,紧接着被调用。作用:用来初始化Filter。
  • doFilter():每一次拦截请求时都会调用。
  • destroy方法在项目停止时调用,用来在对象被销毁前做一些收尾工作。

常用配置项urlPatterns

  • 以指定资源匹配。例如"/index.jsp"
  • 以目录匹配。例如"/servlet/*"
  • 以后缀名匹配,例如"*.jsp"
  • 通配符,拦截所有web资源。"/*"

过滤器使用场景

  • 自动登录
  • 统一设置编码格式
  • 访问权限控制
  • 敏感字符过滤等

多个Filter的执行顺序

  • 在web.xml中,filter执行顺序跟<filter-mapping>的顺序有关,先声明的先执行
  • 使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行
  • 如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter

实现字符编码过滤器
①web.xml配置:

<filter>
    <description>配置filter(EncodingFilter)</description>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.zking.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


②URL通配符:
  

  ①/*  所有界面
    ②*.后缀  [*.do] 匹配所有后缀为do的界面

二、 Listener监听器

第三种Servlet程序: 监听Servlet    Listener
    主要功能是负责监听Web的各种操作,当相关的事件触发后将产生事件,并对事件进行处理。

监听器分为3种:

  •     application监听器
  •     session监听器
  •     request监听器

事件:点击、移动、键盘、触摸


1.application监听器  servlet---ServletContext

  实现:ServletContextListener  (常用)
  重写:

  //容器启动时调用
 

 public void contextInitialized(ServletContextEvent event){
    
  }

  //容器消毁时调用

  public void contextDestroyed(ServletContextEvent event){
    
  }


  ServletContextEvent事件方法:
    .getServletContext()    //取得ServletContext对象,即上下文

  例示:
    

event.getServletContext().getContextPath();


2.Session监听器
  实现:HttpSessionListener  (偶尔用)
  重写:
  //session创建时调用

  public void sessionCreated(HttpSessionEvent event){

  }

  //session销毁时调用

  public void sessionDestroyed(HttpSessionEvent event){

  }


  HttpSessionEvent事件方法:
    .getSession()    //取得当前的session

    例示:
    

event.getSession().getId();    //得到session的ID


  


  实现:HttpSessionAttributeListener (不用,性能差)
  重写:
  //增加属性时触发

  public void attributeAdded(HttpSessionBindingEvent event){
    
  }

  //删除属性时触发

  public void attributeRemoved(HttpSessionBindingEvent event){
    
  }

  //替换属性时触发
 

 public void attributeReplaced(HttpSessionBindingEvent event){
    
  }


  HttpSessionBindingEvent事件方法:

  •     .getSession()    //取得session
  •     .getName()    //取得属性的名称
  •     .getValue()    //取得属性的内容

    例示:

  •     event.getSession()    //取得session
  •     event.getName()        //取得属性的名称
  •     event.getValue()    //取得属性的内容

    
3.request监听器
  实现:ServletRequestListener (不用,性能差)
  重写:
  //请求开始时调用

  public requestInitialized(ServletRequestEvent event){

  }

  //请求结束时调用
 

public requestDestroyed(ServletRequestEvent event){

  }


  ServletRequestEvent事件方法:

  •     .getServletRequest()    //取得ServletRequest对象
  •     .getServletContext()    //取得ServletContext对象

    例示:

    event.getServletRequest().getRemoteAddr();    //得到IP地址
    event.getServletContext().getContextPath();    //得到当前路径


为什么说session监听器和request监听器一般都不用?
答:以request监听器为例,如果采用request监听,那就意味着每次请求都要触发一次监听,这大大降低了程率的效率,因此很少用。


web.xml
 

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


  在tomcat执行时被加载。
  正常关闭时被销毁。


实例:监听在线人数
    
分析:创建一个会话就+1,销毁了会话则-1
      (在会话被创建时,往全局变量中+1;会话被关闭时,全局变量-1)
      服务器停止则移除 count


public class AppListrener implements ServletContextListener,HttpSessionListener {
    
    ServletContext app=null;


    
    //容器销毁时调用
 

   public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        System.out.println("容器销毁了");
        
    }


    //容器初始化时调用

    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        System.out.println("容器初始化了");
        app=sce.getServletContext();
        Integer count=0;
        app.setAttribute("cut", count);
        System.out.println(count+"___");
    }


    
    //session创建时调用

    public void sessionCreated(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        System.out.println("session创建时调用");
        Integer count=(Integer)app.getAttribute("cut");
        
        if(count==null){
            count=0;
        }
        count++;
        System.out.println("创建时:"+count);
        ap
p.setAttribute("cut", count);
    }


    
    //session销毁时调用

    public void sessionDestroyed(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        System.out.println("session销毁时调用");
        Integer count=(Integer)app.getAttribute("cut");
        
        if(count==null){
            count=0;
        }
        count--;
        System.out.println("注销时:"+count);
        app.setAttribute("cut", count);
    }


web.xml

 

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


 


总结

# 过滤器和监听器

## 普通程序员与高级程序员工作区别

> 【普通程序员】:业务实现。——增删改查
>
> 【高级程序员】:不负责业务实现细节——业务整理、程序调优。

## 一、Filter过滤器

### 1.1 过滤器概念

> 过滤器:就是在源数据和目的数据之间起过滤作用的组件

```HTML
    过滤器是JavaWeb三大组件之一(servlet、Listener、Filter),它与Servlet很相似,但是过滤器是用来拦截请求的,而不是处理请求的。

    在Web应用中,过滤器是驻留在服务器端的web组件,可以截取客户端和资源之间的请求与响应信息,且对这些信息进行过滤。WEB开发人员通过Filter技术,对web服务器管理的所有web资源(JSP,Servlet,静态图片文件或静态html文件等)进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制。过滤敏感词汇、压缩响应信息等一些高级功能。

    当用户请求某个Servlet时,会先执行部署在这个请求上的Filter,如果Filter"放行",那么会继承执行用户请求的Servlet;如果Filter不"放行",那么久不会执行用户请求的Servlet。可以理解为,Filter来决定是否调用Servlet。当执行完成Servlet的代码后,还会执行Filter后面的代码。
```

### 1.2 过滤器的作用

**统一编码,统一用户认证,屏蔽非法文字,进行响应数据压缩**

```html
1.对用户进行统一认证,对用户的请求进行记录和审核
    用户认证就是判断一个用户是否为合法用户的过程。
    目前用户认证大都是基于Cookie、Session实现的。注册、登录几乎是所有web站点都具备的两个功能。以商城系统为例,用户输入登录名、密码进行注册、登录,这样系统内就可以为用户保存如:购物车、订单、商品喜好等个性化信息。用户认证使用户授权的基础。以商城系统为例,商家需要先进行用户认证,系统才能判断它是否有某个店铺的管理权。API调用和网页浏览一样,也需要用户认证。

2.对用户传输的数据过滤和替换,转换图像格式,对响应内容进行压缩,减少网络传输。
    每个请求和响应对象都需要设置字符编码,可以通过过滤器来对所有请求和响应对象进行过滤,进行字符集设置。
    
3.对请求和响应进行加密和解密
```

### 1.3过滤器的实现步骤

> - 声明过滤器类并让其实现Filter接口;重写指定方法
> -  在web.xml配置过滤器

```
    Servlet API提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则吧这个Java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
    
    Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
    (1)调用目标资源之前,让一段代码执行。
    (2)是否调用目标资源(即是否让用户访问web资源)
    (3)调用目标资源之后,让一段代码执行。
    
    web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChiain对象是filter接口中最重要的一个对象,它也提供了doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
```

#### 1.3.1 实现javax.servlet.Filter接口

```java
public class FilterDemo1 implements Filter{}
```

#### 1.3.2 实现接口中的抽象方法

```java
/**
  * 过滤器对象被销毁之前执行的方法 
  *     服务器关闭或重载时,过滤器对象销毁
  */
public void destroy() {
    System.out.println("----过滤器被销毁----");
}
/**
  * 对请求进行过滤的方法 
  *     ServletRequest 请求对象 
  *     ServletResponse 响应对象 
  *     FilterChain 过滤链对象
  *         作用:将通过此过滤器的请求,传递给下一个过滤器或目标Servlet
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
    System.out.println("----过滤器执行过滤方法----");
    System.out.println("----DemoFilter-doFilter执行之前----");
    // 对请求放行的方法
    chain.doFilter(request, response);
    System.out.println("----DemoFilter-doFilter执行之后----");
}
/**
  * 过滤器对象创建之后执行的方法,对过滤器进行初始化 
  *     其中FilterConfig参数为当前过滤器的配置,获取过滤器的初始化参数
  *     当服务器启动时,过滤器对象被创建
*/
public void init(FilterConfig fConfig) throws ServletException {
    System.out.println("----过滤器初始化----");
}
```

#### 1.3.3 在web.xml配置过滤器

```xml
<filter>
      <filter-name>FilterDemo</filter-name>
      <filter-class>com.zking.filter.FilterDemo</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>FilterDemo</filter-name>
      <!-- 
          过滤路径
              1.具体路径:某个Servlet的路径
                  例如:/hello
              2.路径后缀统配:*.后缀名
                  例如: *.do    *.html  *.action
              3.全路径统配: /*
                  过滤所有的请求,包括请求Servlet,jsp,以及请求静态资源
      -->
      <url-pattern>/*</url-pattern>
</filter-mapping>
```

#### 1.3.4 Filter生命周期

**过滤器的声明周期:实例化过滤器——>初始化——>过滤——>销毁**

- Filter实例化:web应用程序启动时,web服务器将创建Filter的实例对象,filter对象只会创建一次
- 初始化:当Filter对象实例化之后,就会调用init方法,完成对象的初始化功能,init方法也只会执行一次
- 过滤:Filter会对配置了过滤器的资源的请求和响应进行过滤,而且是每次请求和响应都会进行过滤
- 销毁:当应用服务器重启或停止,都会执行destroy方法销毁Filter,destroy方法在Filter的生命周期中仅执行一次。

```
Filter的创建:
    Filter的创建和销毁由WEB服务器负责。web应用程序启动时,web服务器将创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求做好拦截的准备工作,filter对象指挥创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

Filter的销毁:
    web容器调用destdestroyory方法销毁Filter。destroy方法在Filter的生命周期中仅执行一次。在destroy方法中,可以释放过滤器使用的资源。
```

#### 1.3.5 FilterConfig配置对象

​    用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。

​    因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:

```java
//得到filter的名称
String getFilterName()
//返回在部署描述中执行名称的初始化参数的值。如果不存在返回null
String getInitParameter(String name)   
//返回过滤器的所有初始化参数的名字的枚举集合
Enumeration getInitParameterNames()    
//返回Servlet上下文对象的引用
public ServletContext getServletContext()  
```

### 1.4 过滤器在WEB应用中实例

#### 1.4.1 字符编码过滤器

在之前的学习中,我们需要在每个Servlet里面对请求和响应对象设置字符集,name代码重用性低,不利于维护,而且还存在硬编码的问题;name我们可以把对请求和响应设置字符集的代码放在过滤器里面,提高代码重用性;把字符集相关的配置放在过滤器的初始化参数,解决硬编码问题。

```java
package com.zking.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;

/**
 * 字符编码过滤器 
 *     对所有的请求和响应对象设置字符集 
 *     用来提高代码复用性和解决硬编码问题。
 * 
 * @author zkingzz
 *
 */
public class EncodingFilter implements Filter {

    private String encoding;

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("------字符集过滤器开始过滤------");
        // 对请求对象设置字符集
        servletRequest.setCharacterEncoding(encoding);
        // 对相应对象设置字符集
        servletResponse.setCharacterEncoding(encoding);
        servletResponse.setContentType("text/html;charset=" + encoding);
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String encoding = filterConfig.getInitParameter("encoding");
        this.encoding = encoding;
    }

}

```

```xml
<filter>
    <filter-name>EncodingFilter</filter-name>
      <filter-class>com.zking.filter.EncodingFilter</filter-class>
      <!-- 把过滤器要用到的字符集定义在初始化参数里面 -->
      <init-param>
          <param-name>encoding</param-name>
          <param-value>utf-8</param-value>
      </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
</filter-mapping>
  
```

## 二、Listener监听器

```
先理解什么是监听器,可能某些初学者  听到监听器这个词汇会感到有些不明觉厉,实际上监听器这种东西没各位想象的那么复杂;
监听器就是一个专门用于:对其它对象产生的特定事件,或状态改变后进行监听和相应处理的对象
监听器其实就是一个实现特定接口的普通 Java程序,这个程序专门用于监听另一个 Java对象的方法调用或属性改变。
当被监听对象发生上述事件后,监听器某个方法立即被执行。

使用场景:
    (1)统计在线人数和在线用户
    (2)系统启动时加载初始化信息
    (3)统计网站访问量
    (4)跟Spring结合,做相关操作
```

### 2.1 介绍

- 来自于servlet规范中【一组接口,8个】
- 专门用于监听【域对象生命周期变化】以及【域对象共享数据变化情况】
- 监听器接口实现类,只能由开发人员负责实现

```
JavaWeb中的监听器是 Servlet规范中定义的一种特殊类,它用于监听 Web应用程序中的 ServletContext、 HttpSession和 ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。
```

**域对象**

> 在某一个范围之内,可以为servlet之间提供共享数据的对象。
>
> 分类:
>
> - ServletContext application 全局作用域对象
>   - 在tomcat运行期间,可以为当前工程中所有的Servlet提供共享数据。
> - HttpSession session 会话作用域对象
>   - 在一次会话过程中,为参与本次会话的Servlet提供共享数据
> - HttpServletRequest request 请求作用域对象   
>   - 在一次请求过程中,比如【请求转发】,为参与本次请求的所有Servlet提供共享数据

### 2.2 案例:统计在线人数Demo

```java
// 实现一个用于监听当前 Web服务中 Session数量的监听器;可以简单的理解为统计在线人数

// 首先我们需要实现 Servlet规定的监听器接口
public class OnlineCountListener implements HttpSessionListener {
    // 实现该接口后会必须重写下面这两个方法
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        // 该方法是会在 Session创建时被调用,也就是 Session创建的监听事件

        // 拿到上下文对象
        ServletContext context = se.getSession().getServletContext();
        Integer onlineCount = (Integer) context.getAttribute("onlineCount");
        // 在触发 Session创建监听事件时,如果 onlineCount变量为 0我们将其设置为 1,表示第一个用户在线
        if (onlineCount==null){
            onlineCount = new Integer(1);
            // 如果不为 0表示之前有用户在线,我们将在线人数 +1
        }else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count+1);
        }
        // 打印输出 方便测试,可以去掉
        System.out.println(onlineCount);
        // 将在线人数的变量赋值添加到上下文对象中,方便前端取值
        context.setAttribute("onlineCount",onlineCount);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        // 这个方法则相反,会在Session被销毁时调用

        // 销毁部分则逻辑相反
        ServletContext context = se.getSession().getServletContext();
        Integer onlineCount = (Integer) context.getAttribute("onlineCount");
        if (onlineCount==null){
            onlineCount = new Integer(0);
        }else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count-1);
        }
        context.setAttribute("onlineCount",onlineCount);
    }
}

```

#### 注册监听器

> 监听器写完之后我们需要和过滤器一样,将其注册到 web.xml中才能生效

```xml
<listener>
    <!--过滤器的注册相对比较简单-->
    <listener-class>com.molu.listener.OnlineCountListener</listener-class>
</listener>

```

我们再写一个 jsp,让它能够拿到并展示在线人数

```
<body>
<div>
    <h2>当前网站在线人数为:
        <span style="background-color: aquamarine">
            ${applicationScope.get("onlineCount")}
        </span>
    </h2>
</div>
</body>

```

Filter过滤器它是JavaWeb的三大组件之一。三大组件:servlet程序、Listener监听器、Filter过滤器

Filter过滤器它是JavaEE的规范,也就是接口。

Filter过滤器它的作用:拦截请求,过滤响应。

拦截请求常见的应用场景有:

权限检查、日记操作、事务管理、......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值