JavaWeb Listener监听器的简单介绍及应用

监听器就是一个java类,用来起到监听作用,当java中的其他组件或功能在发生变化时,所作出的提醒或操作。

Listener监听器都能监听什么

• 监听域对象的创建与销毁

• 监听域对象属性值创建更换与销毁

监听器的创建步骤

监听三个域对象的创建和销毁

• ServletContextListener

• ServletRequestListener

• HttpSessionListener

详解JavaWeb中的Listener监听器

1、基本概念

JavaWeb里面的listener是通过观察者设计模式进行实现的。对于观察者模式,这里不做过多介绍,大概讲一下什么意思。

观察者模式又叫发布订阅模式或者监听器模式。在该模式中有两个角色:观察者和被观察者(通常也叫做主题)。观察者在主题里面注册自己感兴趣的事件,当这个事件发生时,主题会通过回调接口的方式通知观察者。

举个生活中的例子:订阅报纸。任何一个家庭或个人都可以向报社订阅报纸。这里报社就是“主题”,家庭就是“观察者”。比如家庭需要订阅明天早晨的报纸,这个就是“事件”。到了第二天早晨,报纸生产出来了,这个就是“事件发生”。当事件发生时,送报员将报纸送到家庭的信箱里面,这里的信箱就是“回调接口”。

对于JavaWeb里面的监听器,Servlet规范定义了一些列的Listener接口类,通过接口类的方式将事件暴露给应用程序,应用程序如果想监听其感兴趣的事件,那么不必去直接注册对应的事件,而是编写自己的listener实现相应的接口类,并将自己的listener注册到servlet容器。当程序关心的事件发生时,servlet容器会通知listener,回调listener里面的方法。这里自定义的listener就是观察者,servlet容器就是主题。

2、样例分析

上面说了,servlet容器是通过Listener接口类将事件暴露给应用程序的。所以我们与其说是注册事件,不如说是注册监听器。对应到编程步骤就是:1.编写自己的listener,实现特定的Listener接口。2.在web.xml里面注册自己的listener。这里以最简单的监听器接口ServletContextListener举例:

先在web.xml进行以下配置

<!--监听器 -->
 <listener>
	<listener-class>com.listener.TestListener1</listener-class>
</listener>

举例代码如下:

package com.listener;

import java.io.FileWriter;

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

public class TestListener1 implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("ServletContextListener.contextInitialized");
	}

	
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		FileWriter fw = null;
        try {
        	//需要在D盘创建listener.txt文件
            // 定义FileWriter对象,关联文件D:\listener.txt,用来向文件写内容
            fw = new FileWriter("D:\\listener.txt", true);
            String line = "ServletContextListener.contextDestroyed函数调用";
            // 每次读取一行内容,循环读取,读到文件末尾结束
            System.out.println("文件内容: " + line);
            fw.write(line);
             // 刷新缓冲流,
            fw.flush();
        }catch(Exception e){
        	e.printStackTrace();
        }
		System.out.println("ServletContextListener.contextDestroyed");
	}

}

当容器启动时会向日志中输出"ServletContextListener.contextInitialized",当容器关闭时会输出"ServletContextListener.contextDestroyed"。详细的解释后面会进一步分析。

这里需要注意是,如果在IDE(Eclipse、STS等)演示上面的例子,当启动服务器时,在控制台可以看到"ServletContextListener.contextInitialized",当关闭服务器时,是看不到"ServletContextListener.contextDestroyed"的。这不是没有执行contextDestroyed方法,而是IDE实现的不够完美。要想验证确实调用了contextDestroyed,可以在contextDestroyed里面写一段代码逻辑往文件输出内容而不要输出到控制台。

启动然后关闭服务器,效果如下:

启动时看到的信息:

 

我们用Tomcat运行一下,效果如下:

运行时候的信息成功输出:

然后我们关闭Tomcat:

可以看到,listener.txt文件写入了  ”ServletContextListener.contextDestroyed函数调用”。

3、源码分析

现在我们分析下,servlet规范为我们定义了哪些事件。更准确的说是定义了哪些监听接口。下面的介绍都是以servlet3.0规范为准。

servlet3.0为我们提供了8个监听器接口,按照它们的作用域来划分的话可以分为三类:

① servlet上下文相关监听接口,包括:ServletContextListener和ServletContextAttributeListener。

② http session相关监听接口,包括:HttpSessionListener、HttpSessionActivationListener、HttpSessionAttributeListener和HttpSessionBindingListener。

③ servlet request相关监听接口,包括:ServletRequestListener和ServletRequestAttributeListener。

其实从接口的命名,各位看官应该能猜出其基本功能。下面我们按分类来解释。

ServletContextListener

servlet上下文监听器接口,对应着两个事件:servlet上下文初始化事件和servlet上下文即将关闭事件。

当web应用初始化的时候,servlet容器会构造ServletContextEven实例,并回调contextInitialize方法。

当servlet上下文即将关闭时,一般是关闭服务器之前,servlet容器会构造ServletContextEven实例,并回调contextDestroyed方法。这里需要注意的是,contextDestroyed方法的执行会在所有的servlet和filter执行完destroy方法之后。

所以如果我们想在应用启动或关闭时需要做些事情的话,就编写自己的listener实现该接口。

所有的事件监听器也是一个模子,按照servlet规范定义相应的事件回调接口方法,方法的入参就是相应的事件源实例。所以我们后面讲解监听器的地方也一带而过。

下面是ServletContextListener的源代码:

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package javax.servlet;

import java.util.EventListener;

/** 
 * Interface for receiving notification events about ServletContext
 * lifecycle changes.
 *
 * <p>In order to receive these notification events, the implementation
 * class must be either declared in the deployment descriptor of the web
 * application, annotated with {@link javax.servlet.annotation.WebListener},
 * or registered via one of the addListener methods defined on
 * {@link ServletContext}.
 *
 * <p>Implementations of this interface are invoked at their
 * {@link #contextInitialized} method in the order in which they have been
 * declared, and at their {@link #contextDestroyed} method in reverse
 * order.
 *
 * @see ServletContextEvent
 *
 * @since Servlet 2.3
 */
public interface ServletContextListener extends EventListener {

    /**
     * Receives notification that the web application initialization
     * process is starting.
     *
     * <p>All ServletContextListeners are notified of context
     * initialization before any filters or servlets in the web
     * application are initialized.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being initialized
     *
     * @implSpec
     * The default implementation takes no action.
     */
    default public void contextInitialized(ServletContextEvent sce) {}

    /**
     * Receives notification that the ServletContext is about to be
     * shut down.
     *
     * <p>All servlets and filters will have been destroyed before any
     * ServletContextListeners are notified of context
     * destruction.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being destroyed
     *
     * @implSpec
     * The default implementation takes no action.
     */
    default public void contextDestroyed(ServletContextEvent sce) {}
}

EventListener

EventListener是一个标记接口,所有的事件监听器都必须继承这个接口,这就是servlet规范,没什么好解释的。下面是EventListener的源代码:

/*
 * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

EventObject

和EventListener类似,EventObject是个事件顶级类,所有具体的事件类都必须继承EventObject。

之前在介绍Servlet的时候,我们解释过一个web应用对应一个servlet上下文。所以ServletContextListener和ServletContextAttributeListener监听的事件的生命范围是贯穿整个web应用的。下面是这两个接口的类图层级关系。

下面是EventObject的源代码:

/*
 * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * <p>
 * The root class from which all event state objects shall be derived.
 * <p>
 * All Events are constructed with a reference to the object, the "source",
 * that is logically deemed to be the object upon which the Event in question
 * initially occurred upon.
 *
 * @since JDK1.1
 */

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }

    /**
     * The object on which the Event initially occurred.
     *
     * @return   The object on which the Event initially occurred.
     */
    public Object getSource() {
        return source;
    }

    /**
     * Returns a String representation of this EventObject.
     *
     * @return  A a String representation of this EventObject.
     */
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

这个类很简单,其本质就一个东西:source。通过类名EventObject和属性名source,就能看出这个类就干了一件事,持有“事件源对象”。

ServletContextEvent

servlet上下文事件,这个事件类就是对EventObject的简单继承。构造方法中提供ServletContext实例作为事件源。因为事件源是servlet上下文,所以提供个getServletContext获取ServletContext实例。

在我们后续讲解其他事件类的时候,都是一个模子,每个事件类都提供相应的构造方法,传入相应的事件源对象,并提供额外的获取事件源方法。所以EventObject就是个事件源的基类,所有事件子类的本质就干了一件事,确定具体的事件源对象。

所以我们后面讲解事件的地方,一带而过。下面是ServletContextEvent的源代码:

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值