Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)

监听器介绍

监听器的概念

监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。
监听器其实就是一个实现特定接口的普通Java程序,这个程序专门用于监听另一个Java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行。Java中的事件监听机制可用下图来表示:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第1张图片

一道面试题:请描述一下Java事件监听机制

  1. Java的事件监听机制涉及到三个组件:事件源、事件监听器、事件对象;
  2. 当事件源上发生操作时,它将会调用事件监听器的一个方法,并在调用这个方法时,会传递事件对象过去;
  3. 事件监听器由开发人员编写,开发人员在事件监听器中,通过事件对象可以拿到事件源,从而对事件源上的操作进行处理。

监听器典型案例——监听window窗口的事件监听器

package cn.liayun.demo;

import java.awt.Frame;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

public class Demo1 {

	public static void main(String[] args) {
		Frame f = new Frame();//代表一个window窗口
		f.setSize(400, 400);
		f.setVisible(true);
		
		//把监听器注册到事件源上
		f.addWindowListener(new MyListener());
	}

}

//编写一个监听器
class MyListener implements WindowListener {

	@Override
	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	/*
     * 当window窗体关闭时,MyListener这个监听器就会监听到,
     * 监听器就会调用windowClosing方法处理window窗体关闭时的动作
     */
	@Override
	public void windowClosing(WindowEvent e) {
		Frame f = (Frame) e.getSource();//拿到事件源
		f.dispose();//关闭窗体
	}

	@Override
	public void windowClosed(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	//window窗口从正常状态变为最小化状态时调用
	@Override
	public void windowIconified(WindowEvent e) {
		System.out.println("窗口最小化了!");
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	
}

观察者设计模式(observer设计模式)

我们平时做开发的时候,我们是写监听器去监听其他对象,那么我们如果想设计一个对象,让这个对象可以被别的对象监听又该怎么做呢?这时我们可以使用观察者设计模式按照严格的事件处理模型来设计一个对象,这个对象就可以被别的对象监听了,事件处理模型涉及到三个组件:事件源、事件对象和事件监听器。
我们一开始可能会简单地设计这样一个Person对象:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第2张图片
这时,如果Person对象想要被别人监听,那么该Person对象就要允许别人往你这个对象上注册一个监听器。假设人家注册了一个监听器进来,那么该监听器由谁调用呢?监听器由事件源调用,事件源上发生动作,它调用监听器的方法。也就是说别人调用Person对象的一个方法注册一个监听器,等会儿Person对象就要调用监听器的方法,也即Person对象现在写的程序要调用别人将来写的程序,那么它就要对外暴露一个接口。
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第3张图片
对外暴露一个接口,别人实现这个接口,Person对象针对这个接口进行调用,那么这个接口里定义几个方法呢?Person这个对象身上有2个动作想被别人监听,所以这个接口应针对这2个动作定义2个相对应的方法。此时,Person对象的具体代码应为:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第4张图片
但这样设计出来的东西少了什么呢?事件处理涉及到3个组件,而现在只涉及到2个组件,一个事件源和一个事件监听器,少了事件对象。事件对象在事件监听机制里面主要是用于封装事件源的。也就是说在调用事件源的run()方法时,就会调用监听器的dorun()方法,在调用该方法时,应该传一个事件对象过去,通过这个事件对象,把到底是哪个事件源上发生的操作传过去。此时,我们按照事件处理模型设计的Person对象的完整代码如下所示:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第5张图片
经过这样的设计之后,Person类的对象就可以被其他对象监听了。测试代码如下:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第6张图片
运行结果如下:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第7张图片

JavaWeb中的监听器

基本概念

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

Servlet监听器的分类

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContext,HttpSession和ServletRequest这三个域对象。Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第8张图片

监听ServletContext域对象的创建和销毁

ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。实现了ServletContextListener接口的类都可以对ServletContext对象的创建和销毁进行监听。

  • 当ServletContext对象被创建时,激发contextInitialized(ServletContextEvent sce)方法;
  • 当ServletContext对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。

那么会有一个问题:ServletContext域对象何时创建和销毁呢?
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第9张图片

案例:编写一个MyServletContextListener类,实现ServletContextListener接口,监听ServletContext对象的创建和销毁。

首先,编写一个监听器,代码如下:

package cn.liayun.web.listener;

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

public class MyServletContextListener implements ServletContextListener {

	/*
	 * 当ServletContext被创建的时候,下面方法执行
	 * (什么时候创建ServletContext呢?将Web工程发布到Web服务器里面去了,只要一启动Web服务器,
	 * Web服务器会针对每一个Web应用创建ServletContext)
	 */
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("ServletContext被创建了!!!");
	}

	/*
	 * 当ServletContext被销毁的时候,下面方法执行
	 * (停止服务器,服务器就会把针对于每一个Web应用的ServletContext摧毁)
	 */
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("ServletContext被销毁了!!!");
	}

}

然后,在web.xml文件中注册监听器。MyServletContextListener监听器想要能工作,必须注册到事件源上面去,以前我们注册到事件源上面去,都是自己调方法,往事件源上面注册。现在就不能这么干了,想要把监听器注册到ServletContext这个事件源上面去,只须告诉Tomcat服务器,Tomcat服务器会自动帮你注册。监听器也属于Web资源,只要涉及到对Web资源的配置,都要找web.xml文件。

<listener>
	<listener-class>cn.liayun.web.listener.MyServletContextListenerlistener-class>
listener>

经过这两个步骤,我们就完成了监听器的编写和注册了,Web服务器在启动时,就会自动把在web.xml中配置的监听器注册到ServletContext对象上,这样开发好的MyServletContextListener监听器就可以对ServletContext对象进行监听了。在以上过程中,大家要注意以下三点:

  1. 和编写其它事件监听器一样,编写Servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法;
  2. 和其它事件监听器略有不同的是,Servlet监听器的注册不是直接注册在事件源上,而是由Web容器负责注册,开发人员只需在web.xml文件中使用标签配置好监听器,Web容器就会自动把监听器注册到事件源中;
  3. 一个web.xml文件中可以配置多个Servlet事件监听器,Web服务器按照它们在web.xml文件中的注册顺序来加载和注册这些Serlvet事件监听器。

这个技术在开发里面用在哪里呢?这才是我们所关心的,即什么情况下我们才需要监听ServletContext对象的创建和销毁呢?其实可以这样去理解这个监听器,这个监听器说白了可以监听到Web应用的启动和销毁,在做实际开发时,有时候希望Web应用启动时,就初始化一些资源,那么就把初始化一些资源的代码写到contextInitialized方法里面。具体的案例有:

  1. Web应用一启动时,希望启动一些定时器来定时的执行某些任务。只要把启动定时器的代码写到contextInitialized方法里面,这个Web应用一启动,定时器就启动了;
  2. 我们以后会学Spring框架,其实Spring的启动代码就是写在一个ServletContext监听器的contextInitialized方法里面的。Spring是一个框架,我们希望Web应用一启动的时候,就把Spring框架启动起来。

监听HttpSession域对象的创建和销毁

HttpSessionListener接口用于监听HttpSession对象的创建和销毁。

  • 创建一个Session时,激发sessionCreated(HttpSessionEvent se)方法;
  • 销毁一个Session时,激发sessionDestroyed(HttpSessionEvent se)方法。

那么会有一个问题:Session域对象何时创建和销毁呢?
在这里插入图片描述

案例:编写一个MyHttpSessionListener类,实现HttpSessionListener接口,监听HttpSession对象的创建和销毁。

首先,编写一个监听器,代码如下:

package cn.liayun.web.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyHttpSessionListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		System.out.println("session被创建了!!");
		System.out.println("创建好的HttpSession的id是:" + se.getSession().getId());
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		System.out.println("session被销毁了!!");
	}

}

然后,在web.xml文件中注册监听器。
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第10张图片
当我们访问某个jsp页面时,HttpSession对象就会创建,此时就可以在HttpSessionListener观察到HttpSession对象的创建过程了,我们可以写一个myindex.jsp页面观察HttpSession对象创建的过程。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HttpSessionListener监听器监听HttpSession对象的创建title>
head>
<body>
	一访问myindex.jsp页面,HttpSession就创建了,创建好的Session的Id是:${pageContext.session.id}
body>
html>

运行结果如下:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第11张图片
由于在web.xml里面配置了session的失效时间,所以在一分钟之后session将被销毁。在以上过程中,大家要注意以下两点:

  1. 现在在客户端把Cookie给禁用了,那么再次点击刷新按钮,有没有session被创建?答案是有的,现在在客户端把Cookie给禁了,意味着你再去访问服务器,没有带sessionid号过去,服务器看你没有带sessionid,它认为你又是一个新的来访者,它又会帮你创建session;
  2. 现在将浏览器窗口关了,能不能看见session被销毁?答案是不能,session会驻留在内存里面,30分钟没人用了服务器才将其摧毁。

这个技术在开发里面用在哪里呢?这才是我们所关心的。可以用于统计当前在线人数。一般来说,用户都会开一个浏览器访问服务器,只要他一访问,服务器就会针对他创建一个session,每个用户就有一个session,在实际开发里面,只要统计内存里面有多少session,就能知道当前有多少在线人数了。为了统计当前在线人数,这时可以写一个这样的监听器,只要有一个session被创建就让一个变量count加上1,session被销毁就让变量count减去1,输出count这个值,就能知道当前有多少在线人数了。这一技术在实际开发中的应用可参见我的博客《Java Web基础入门第八十二讲 Listener(监听器)——监听器在开发中的应用(一)》,注意:统计出来的当前在线人数只是一个近似值。

监听ServletRequest域对象的创建和销毁

ServletRequestListener接口用于监听ServletRequest对象的创建和销毁。

  • request对象被创建时,监听器的requestInitialized(ServletRequestEvent sre)方法将会被调用;
  • request对象被销毁时,监听器的requestDestroyed(ServletRequestEvent sre)方法将会被调用。

那么会有一个问题:request域对象何时创建和销毁呢?
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第12张图片

案例:编写一个MyServletRequestListener类,实现ServletRequestListener接口,监听ServletRequest对象的创建和销毁。

首先,编写一个监听器,代码如下:

package cn.liayun.web.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class MyServletRequestListener implements ServletRequestListener {

	@Override
	public void requestDestroyed(ServletRequestEvent sre) {
		System.out.println("request被创建了!!!");
		sre.getServletRequest().getRemoteAddr();//监听被哪些浏览器访问
	}

	@Override
	public void requestInitialized(ServletRequestEvent sre) {
		System.out.println("request被销毁了!!!");
	}

}

然后,在web.xml文件中注册监听器。

<listener>
	<listener-class>cn.liayun.web.listener.MyServletRequestListenerlistener-class>
listener>

测试结果如下:
Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第13张图片
从运行结果中可以看到,用户每一次访问都会创建request对象,当访问结束后,request对象就会销毁。
这个技术在开发里面用在哪里呢?这才是所我们关心的。这个监听器可以用来做网站性能统计,针对每一个请求,都会有一个request对象创建。

  1. 我们可以在requestInitialized(ServletRequestEvent sre)方法里面加上一句代码:count++,如下:
    Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第14张图片
    输出count的值,就可以统计网站一天的点击量。
  2. 我们也可以在requestInitialized(ServletRequestEvent sre)方法里面加上一句代码:sre.getServletRequest().getRemoteAddr();,如下:
    Java Web基础入门第八十讲 Listener(监听器)——Servlet中的事件监听器(上)_第15张图片
    可以知道当前这个请求是由哪个IP发出来的,在后台可以通过这个监听器监听到哪些IP在给你发请求,这样做的目的是为了防止坏人,有些坏人恶意点击,比如写机器人点击,在后台写这样的一个监听器可以监听到某个时间段有某个IP重复点击,如果发生这种情况,就说明这个人是坏人,就可以屏蔽其IP了。

你可能感兴趣的:(Java,Web基础入门)