04 Listener和Filter


 javaEE包括13门规范 在课程中主要学习 servlet技术 和 jsp技术
 其中 servlet规范包括三个技术点:servlet  listener  filter

【监听器 Listener】


 1.什么是监听器?
 监听器就是监听某个对象的的状态变化的组件
 监听器的相关概念:
 事件源:被监听的对象  ----- 三个域对象 request  session  servletContext
 监听器:监听事件源对象  事件源对象的状态的变化都会触发监听器 ---- 6+2
 注册监听器:将监听器与事件源进行绑定
 响应行为:监听器监听到事件源的状态变化时 所涉及的功能代码 ---- 程序员编写代码

 2.监听器(接口)有哪些?

 3.监听三大域对象的创建与销毁的监听器编写步骤(重点):
   a、编写一个监听器类去实现监听器接口
   b、覆盖(重写)监听器的方法
   c、需要在web.xml中进行配置---注册

例子:银行每隔24小时就要计算利息一次

 

package com.bwf.listener;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.sun.beans.util.Cache;

public class MyServletContextListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("ServletContext创建了");
		// 获得触发事件的域对象
		// sce.getServletContext();

		// 银行系统, 每天的24点计息一次
		// 想获得明天的0点的date
		
		Calendar time = Calendar.getInstance();
		time.add(Calendar.DATE, 1);
		time.set(Calendar.HOUR, 0);
		time.set(Calendar.MINUTE, 0);
		time.set(Calendar.SECOND, 0);
		time.set(Calendar.MILLISECOND, 0);
		
		// void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
		// 安排指定的任务在指定的时间开始进行重复的固定速率执行。
		new Timer().scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				System.out.println("银行计息拉");
			}
		}, time.getTime(), 24 * 60 * 60 * 1000);

	}

	// 获得24小时以后的时间,不能这么写,这样的话主线程休眠24小时了
	// public static Date get24(){
	// try {
	// Thread.sleep(24 * 60 * 1000);
	// } catch (InterruptedException e) {
	// e.printStackTrace();
	// }
	// return new Date();
	// }

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("ServletContext销毁了");

	}

}

配置文件(注册监听器):

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

 4.监听三大域对象的属性变化的
   (1)域对象的通用的方法:
      setAttribute(name,value)
       --- 触发添加属性的监听器的方法   
       --- 触发修改属性的监听器的方法 
      removeAttribute(name)  
       --- 触发删除属性的监听器的方法 

      getAttribute(name)

   (2)ServletContextAttibuteListener监听器
      attributeAdded
      attributeRemoved
      attributeReplaced

这里以ServletContextAttributeListener为例:

写一个监听类实现ServletContextAttributeListener接口,重写attributeAdded,attributeRemoved,attributeReplaced三个方法

package com.bwf.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

public class MyServletContextAttrListener implements ServletContextAttributeListener {

	@Override
	public void attributeAdded(ServletContextAttributeEvent scae) {
		System.out.println("添加了一个属性");
		// 获得域对象
		ServletContext context = scae.getServletContext();
		// 添加的属性名
		String name = scae.getName();
		// 添加的值
		Object value = scae.getValue();
		System.out.println("name = " + name + " value = " + value);
	}

	@Override
	public void attributeRemoved(ServletContextAttributeEvent scae) {
		//这个方法传入的k-v是原来的
		System.out.println("删除了一个属性");
		// 移除的属性名
		String name = scae.getName();
		// 移除的属性值
		Object value = scae.getValue();
		System.out.println("name = " + name + " value = " + value);
	}

	@Override
	public void attributeReplaced(ServletContextAttributeEvent scae) {
		System.out.println("修改了一个属性");
		// 添加的属性名
		String name = scae.getName();
		// 原来的值
		Object value = scae.getValue();
		// 新的值
		Object value2 = scae.getServletContext().getAttribute(name);

		System.out.println("name = " + name + " value = " + value);
		System.out.println("value2 = " + value2);
	}

}

并在web.xml文件中注册监听器

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

监听器买好了,接下来就是创建一个被监视者(这个监视者要对servletcontext域对象的内容产生过改变)

package com.bwf.servlet;

import java.io.IOException;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/t1")
public class Test1Servlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// 往ServletContext中放入一个数据(随机数)
		getServletContext().setAttribute("num", new Random().nextInt(100));
	}

}

可以看到,如果多次访问了t1这个servlet的doGet方法,那么监听器第一次会调用attributeAdded方法,后面都是调用attributeReplaced方法

添加了一个属性
name = num value = 81
修改了一个属性
name = num value = 81
value2 = 84
修改了一个属性
name = num value = 84
value2 = 84
修改了一个属性
name = num value = 84
value2 = 45
修改了一个属性
name = num value = 45
value2 = 93
修改了一个属性
name = num value = 93

如果servletContext调用removeAttribute方法,那么监视器会调用attributeRemoved方法

	// 从ServletContext中移除一个数据
		getServletContext().removeAttribute("num");


   (3) HttpSessionAttributeListener监听器(同上)
   (4) ServletRequestAriibuteListenr监听器(同上)

 5.与session中的绑定的对象相关的监听器(对象感知监听器)
  (1)即将要被绑定到session中的对象有几种状态

    绑定状态:就一个对象被放到session域中
    解绑状态:就是这个对象从session域中移除了

    钝化状态:是将session内存中的对象持久化(序列化)到磁盘
    活化状态:就是将磁盘上的对象再次恢复到session内存中

  (2)绑定与解绑的监听器HttpSessionBindingListener


  (3)钝化与活化的监听器HttpSessionActivationListener

实例:user类实现了序列化接口,绑定解绑接口,钝化活化接口,并重写相关方法

package com.bwf.bean;

import java.io.Serializable;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

public class User implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable{

	private String name;
	private int age;
	
	public User() {
		super();
	}
	
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}

	// 绑定 - 被放到session去了
	@Override
	public void valueBound(HttpSessionBindingEvent event) {
		
		System.out.println("我叫" + name + ", 我被 " + event.getSession().getId() + " session绑定了");
		System.out.println(hashCode());
	}

	// 解绑 - 从session中被移除
	@Override
	public void valueUnbound(HttpSessionBindingEvent event) {
		System.out.println("我叫" + name + ", 我被 " + event.getSession().getId() + " session解绑了");
	}

	// 钝化
	@Override
	public void sessionWillPassivate(HttpSessionEvent se) {
		System.out.println("我叫" + name + ", 我会回来的!");
	}

	// 活化
	@Override
	public void sessionDidActivate(HttpSessionEvent se) {
		System.out.println("我叫" + name + ", 我又活了!");
	}
	
	
	
}

t3的servlet往session中放入数据

		// 往Session中放入一个User对象
		
		User u1 = new User("张三", 18);
		User u2 = new User("李四", 19);
		
		request.getSession().setAttribute("user", u2);

可以看到,u2放入session(u1并没有放入)时调用了绑定方法

过了1分钟后,调用了钝化方法,把对象从内存放入了硬盘(持久化)

 

session里移除该对象

// 往Session中解绑user
		request.getSession().removeAttribute("user");

会先调用活化方法,把它从硬盘恢复到内存,再调用解绑方法

   面试题:当用户很多时,怎样对服务器进行优化?   
   
   # 可以通过配置文件 指定对象钝化时间 --- 对象多长时间不用被钝化
     在META-INF下创建一个context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context>
	<!-- maxIdleSwap:session中的对象多长时间不使用就钝化 -->
	<!-- directory:钝化后的对象的文件写到磁盘的哪个目录下 配置钝化的对象文件在 work/catalina/localhost/钝化文件 -->
	<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
		<Store className="org.apache.catalina.session.FileStore" directory="bwf" />
	</Manager>
</Context>

【过滤器 Filter】


 1.filter的简介
  filter是对客户端访问资源的过滤,
  符合条件放行,
  不符合条件不放行,
  并且可以对目标资源访问前后进行逻辑处理

 2. 快速入门
  步骤:

  1. 编写一个过滤器的类实现Filter接口
  2. 实现接口中尚未实现的方法(着重实现doFilter方法)
  3. 在web.xml中进行配置(主要是配置要对哪些资源进行过滤)

 3.Filter的API
  (1)filter生命周期及其与生命周期相关的方法
    Filter接口有三个方法,并且这个三个都是与Filter的生命相关的方法
    init(Filterconfig):代表filter对象初始化方法 filter对象创建时执行
    doFilter(ServletRequest,ServletResponse,FilterCha):
    代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话
    那么每次访问这个资源都会执行doFilter方法
    destory():代表是filter销毁方法 当filter对象销毁时执行该方法

   Filter对象的生命周期:
   Filter何时创建:服务器启动时就创建该filter对象
   Filter何时销毁:服务器关闭时filter销毁

  (2)Filter的AP详解
     1)init(FilterConfig)
      其中参数config代表 该Filter对象的配置信息的对象,内部封装是该filter的配置信息。
     2)destory()方法
      filter对象销毁时执行
     3)doFilter方法
      doFilter(ServletRequest,ServletResponse,FilterChain)
      其中的参数:
      ServletRequest/ServletResponse:每次在执行doFilter方法时web容器负责创建一个request和一个response对象作为doFilter的参数传递进来.该request和该response就是在访问目标资源的service方法时的request和response。
      FilterChain:过滤器链对象,通过该对象的doFilter方法可以放行该请求

package com.bwf.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;

public class Filter1 implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("创建了过滤器");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("过滤方法");
		
		// 放行
		chain.doFilter(request, response);
	}

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

}

 4.Filter的配置(2.5下用xml文件进行配置,3.0用@WebFilter注解进行配置)

   url-pattern配置时
   1)完全匹配  /sertvle1
   2)目录匹配  /aaa/bbb/* ----最多的
   /user/*:访问前台的资源进入此过滤器
   /admin/*:访问后台的资源时执行此过滤器
   3)扩展名匹配  *.abc  *.jsp

  注意:url-pattern可以使用servlet-name替代,也可以混用

例1:2.5servlet下,配置xml文件,对/s1路径的访问进行过滤

  <filter>
  	<filter-name>filter1</filter-name>
  	<filter-class>com.bwf.filter.Filter1</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>filter1</filter-name>
	<url-pattern>/s1</url-pattern> 
  </filter-mapping>

例2:3.0servlet下,注解方式

@WebFilter(dispatcherTypes = {DispatcherType.REQUEST }
                    , urlPatterns = { "/login" })


  # dispatcher:访问的方式
   REQUEST:默认值,代表直接访问某个资源时执行filter
   FORWARD:转发时才执行filter
   INCLUDE: 包含资源时执行filter
   ERROR:发生错误时 进行跳转是执行filter

实战:写一个过滤器,实现:解决全局下doGet方法获得参数的中文乱码

带过滤器的访问流程

思路:

写一个装饰模式,servlet有专门用于继承的类,用来写装饰模式,这里用到HttpServletRequestWrapper类

继承该类,并重写里面的getParameter方法

package com.bwf.filter;

import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class Utf8Request extends HttpServletRequestWrapper {

	public Utf8Request(HttpServletRequest request) {
		super(request);
	}
	
	@Override
	public String getParameter(String name) {
		// 先拿出原来的数据 -> 乱码
		String parameter = super.getParameter(name);
		
		String res = parameter;
		try {
			res = new String(parameter.getBytes("ISO-8859-1"), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		return res;
	}
	
}

写一个过滤器,利用装饰模式强化的getParameter方法,对request参数进行过滤,这个过滤器用@WebFilter注解建立映射关系

package com.bwf.filter;

import java.io.IOException;
import javax.servlet.DispatcherType;
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(dispatcherTypes = {DispatcherType.REQUEST }
					, urlPatterns = { "/login" })
public class EncodingFilter implements Filter {

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
		// request 参数就是后面Servlet会拿到的request
		// 希望增强 
		// 把request包装一下
		Utf8Request nRequest = new Utf8Request((HttpServletRequest) request);
        //放行		
		chain.doFilter(nRequest, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
	}
	public void destroy() {
	}

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值