【Java Web编程 十一】深入理解Servlet监听器

简介: 【Java Web编程 十一】深入理解Servlet监听器

上篇Blog详细介绍了Servlet的过滤器,了解到过滤器类似一个AOP的概念,这篇Blog就来学习下Servlet的监听器,了解下Java Web的第三大组件的用处是什么,又是怎么工作的。

监听器基本概念

在JSP技术中我们学习了4大作用域:pageContext、request、session、application,用于实现数据共享,其中request、session、application三个作用域分别对应于我们

  • HttpServletRequest接口:请求作用域对象
  • HttpSession接口:会话作用域对象
  • ServletContext接口:全局作用域对象

虽然有了数据共享的作用域,但是数据的具体流转过程我们看不到,比如作用域对象什么时候创建和销毁的,什么时候存取、改变和删除的,所以无法在指定的时机对数据和对象进行操作,这个时候就需要Servlet监听器来发挥作用了:

  • 事件:方法调用、属性改变、状态改变等,这里对应对象的创建与销毁事件,属性改变事件,以及额外对HttpSession附加的监听 HttpSession中的对象状态改变事件。
  • 事件源:被监听的对象,这里是HttpServletRequest、HttpSession、ServletContext。
  • 监听器:用于监听事件源对象 ,事件源对象状态的变化都会触发监听器,这里是我们创建的一个Servlet监听器
  • 注册监听器:将监听器与事件源进行绑定

所以一句话描述就是:给事件源注册好监听器后,当某个事件发生引起事件源变化时,监听器监听到这一变化进行逻辑处理

监听器作用

Servlet监听器是Servlet规范中定义的一种特殊类,用于监听servletContext(application)、httpsession(session)、servletRequest(request)三个作用域对象的创建与销毁事件,以及监听这些作用域对象中属性发生修改的事件,说白了就是监听三个作用域对象的生命周期。

创建监听器

用IDEA可以很轻松的创建一个监听器出来,按照如下步骤:

可以看到IDEA自动帮我们生成了监听器代码模板:

package com.example.myfirstweb.controller; /**
 * * @Name ${NAME}
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/7/27
 */
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebListener
public class ServletListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {
    public ServletListener() {
    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        /* This method is called when the servlet context is initialized(when the Web application is deployed). */
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        /* This method is called when the servlet Context is undeployed or Application Server shuts down. */
    }
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */
    }
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
    }
    @Override
    public void attributeAdded(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is added to a session. */
    }
    @Override
    public void attributeRemoved(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is removed from a session. */
    }
    @Override
    public void attributeReplaced(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is replaced in a session. */
    }
}

可以看到监听器是通过注解定义的,我们来看下注解的源代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebListener {
    String value() default "";
}

监听器常用方法

Servlet 规范中定义了 8 个监听器接口,可以用于监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期和属性变化事件。开发 Servlet 监听器需要实现相应的监听器接口并重写接口中的方法:

监听对象创建和销毁的监听器

Servlet 规范定义了监听 ServletContext、HttpSession、HttpServletRequest 这三个对象创建和销毁事件的监听器

监听属性变更的监听器

Servlet 规范定义了监听 ServletContext、HttpSession、HttpServletRequest 这三个对象中的属性变更事件的监听器,这三个监听器接口分别是 ServletContextAttributeListener、HttpSessionAttributeListener 和 ServletRequestAttributeListener。这三个接口中都定义了三个方法,用来处理被监听对象中属性的增加,删除和替换事件。同一种事件在这三个接口中对应的方法名称完全相同,只是参数类型不同

监听 Session中对象状态改变的监听器

Session 中的对象可以有多种状态:绑定到 Session 中、从 Session 中解除绑定、随 Session 对象持久化到存储设备中(钝化)、随 Session 对象从存储设备中恢复(活化)。Servlet 规范中定义了两个特殊的监听器接口,用来帮助对象了解自己在 Session 中的状态:HttpSessionBindingListener 接口和 HttpSessionActivationListener 接口 ,实现这两个接口的类不需要进行注册

监听器实现网站在线人数监听

监听网站的在线人数,也就是统计服务器中的session被创建多少次,创建一次在线人数+1;session销毁一次在线人数-1。

JSP页面

假设登录成功,则跳转到index.jsp页面,和上一篇的过滤器梦幻联动,所有页面请求先到login.jsp,登录后才会跳转,这里我们模拟的是已经登录成功后的跳转:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" errorPage="jsp/error.jsp" %>
<%@ include file="jsp/top.jsp" %>
<!DOCTYPE html>
<html>
<head>
  <title>JSP - Hello World</title>
  <%!
    int number = 0;// 声明全局变量
    int count() {
      number++;//number 自增
      return number;
    }%>
</head>
<h1><%= "Hello World!" %>
</h1>
<br/>
<body>
<%
  String print = "我是脚本语句内容";
  List<String> strList = new ArrayList<>();
  strList.add("脚本语句1");
  strList.add("脚本语句2");
  strList.add("脚本语句3");
  //  strList.add("脚本语句4");  //这是一个Java注释,用来注释Java代码,查看源码时不可见
%>
<a href="hello-servlet">我的第一个JavaWeb项目</a> <!-- 这是一个html注释,可见<%=new Date().getTime() %> -->
<br/>
<%--这是一个JSP注释,查看源码时不可见--%>
声明语句示例:这是第 <%=count()%> 次访问该页面
<br/>
表达式语句示例: <%="我是一个表达式语句"%>
<br/>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>
</body>
</html>

Servlet页面

我们准备两个页面,一个页面用于跳转Servlet到index.jsp,60秒后自动退出:

package com.example.MyFirstJavaWeb;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //假设登录成功
        HttpSession session = request.getSession();
        session.setMaxInactiveInterval(60); //60秒后自动推出
        response.sendRedirect("index.jsp");
    }
    public void destroy() {
    }
}

还有一个用于手动退出session的页面:

package com.example.MyFirstJavaWeb;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet(name = "StopSession", value = "/stop-servlet")
public class StopSession extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //假设退出成功
        HttpSession session = request.getSession();
        session.invalidate();
    }
    public void destroy() {
    }
}

Servlet监听器

接下来就是我们监听器的代码了,在application初始化时我们增加一个数量统计,然后在session创建时进行累加,销毁时累减:

package com.example.MyFirstJavaWeb.listener;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.util.Date;
@WebListener
public class CountListener implements HttpSessionListener,ServletContextListener {
    public CountListener() {
    }
    //监听application销毁事件--即服务器关闭
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("application销毁~~~~~~~~");
    }
    //监听application创建事件--即服务器开启
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("application创建~~~~~~~~");
        int sessionCount = 0;//服务器开启的时候,定义变量用于统计在线人数,保存起来
        ServletContext application = sce.getServletContext();
        application.setAttribute("sessionCount", sessionCount);
    }
    //监听session创建事件--即有人登陆成功,进入项目主页面,则在线人数+1
    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        System.out.println("登陆成功"+hse.getSession().getId()+"创建时间:"+new Date());
        ServletContext application = hse.getSession().getServletContext();
        int sessionCount = (int)application.getAttribute("sessionCount");
        application.setAttribute("sessionCount", ++sessionCount);
        System.out.println("当前在线用户数为:"+application.getAttribute("sessionCount"));
    }
    //监听session销毁事件--销毁有很多种方式:强制销毁、自动过期等,无所谓什么方式,只要session销毁就可以监听到
    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        System.out.println("退出成功"+hse.getSession().getId()+"退出时间:"+new Date());
        ServletContext application = hse.getSession().getServletContext();
        int sessionCount = (int)application.getAttribute("sessionCount");
        application.setAttribute("sessionCount", --sessionCount);
        System.out.println("当前在线用户数为:"+application.getAttribute("sessionCount"));
    }
}

实现效果

这样当我们每次通过不同浏览器访问页面都会增加一个新的用户,整个变化过程如下:

注意要把这个勾掉,否则idea在启动时会额外给你生成一个session,那么启动时就会有两个session了。

总结一下

其实Servlet监听器最大的作用就是被当作状态机,其实我们任何流程中,抽象出来都是在通过事件对事件源的生命周期管理来实现的,对象在事件驱动下在流程中流转。系统设计时也一样,所以其实监听器能让我们感悟更多的其实是事件驱动生命周期这种设计思想,正如过滤器给我们更多的感悟是切面AOP对所有服务的控制一样,更重要的是思想,而组件只是思想的落地。有了思想我们可以应用再更多其它地方。

相关文章
|
3月前
|
Java 应用服务中间件 Spring
【终极解决方案】Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
【终极解决方案】Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
55 0
|
5月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
54 3
|
5月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
49 1
|
5月前
|
C# 数据可视化 开发者
WPF开发者福音:深度解析OxyPlot与LiveCharts图表库,轻松实现数据可视化不再是难题!
【8月更文挑战第31天】在WPF应用中,数据可视化对提升用户体验至关重要。本文介绍并演示了两种流行图表库OxyPlot和LiveCharts的集成与使用方法。OxyPlot是一款适用于.NET应用的开源图表库,提供多种图表类型,易于集成。LiveCharts则以其丰富的图表类型和动画效果,特别适合实时数据展示。通过具体代码示例,本文展示了如何利用这两种图表库创建折线图和柱状图,并详细说明了安装和配置步骤。希望本文能帮助开发者在WPF应用中轻松实现高效、美观的数据可视化。
280 0
|
5月前
|
Java Maven Android开发
解锁Web开发新技能:从零开始的Struts 2之旅——让你的Java编程之路更加宽广,首个应用实例带你飞!
【8月更文挑战第31天】对于初学者,掌握 Struts 2 框架不仅能提升 Web 开发能力,还能深入了解 MVC 架构。Struts 2 是一个基于 Servlet 的 Java 框架,提供表单验证、文件上传、国际化等功能,便于快速构建易维护的 Web 应用。本文通过示例演示如何从零开始搭建环境并创建一个简单的 Struts 2 项目,包括配置 `struts.xml`、编写 Action 类及视图文件,并配置 web.xml。通过这些步骤,你将学会基本的开发流程,为进一步学习高级功能打下基础。
73 0
|
5月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
基于Servlet和JSP的Java Web应用开发指南
126 0
|
5月前
|
前端开发 安全 Java
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
35 0
|
5月前
|
监控 前端开发 Java
揭秘Web开发神器:Servlet、过滤器、拦截器、监听器如何联手打造无敌博客系统,让你的用户欲罢不能!
【8月更文挑战第24天】在Java Web开发中,Servlet、过滤器(Filter)、拦截器(Interceptor,特指Spring MVC中的)及监听器(Listener)协同工作,实现复杂应用逻辑。以博客系统为例,Servlet处理文章详情请求,过滤器(如LoginFilter)检查登录状态并重定向,Spring MVC拦截器(如LoggingInterceptor)提供细粒度控制(如日志记录),监听器(如SessionListener)监控会话生命周期事件。这些组件共同构建出高效、有序的Web应用程序。
46 0
|
7月前
|
自然语言处理 前端开发 Java
Servlet与JSP:Java Web开发的基石技术详解
【6月更文挑战第23天】Java Web的Servlet与JSP是动态网页的核心。Servlet是服务器端的Java应用,处理HTTP请求并响应;JSP则是结合HTML与Java代码的页面,用于动态内容生成。Servlet通过生命周期方法如`init()`、`service()`和`destroy()`工作,而JSP在执行时编译成Servlet。两者在MVC架构中分工,Servlet处理逻辑,JSP展示数据。尽管有Spring MVC等框架,Servlet和JSP仍是理解Web开发基础的关键。
131 12
|
7月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
【6月更文挑战第23天】构建Java Web应用,Servlet与JSP携手打造在线图书管理系统,涵盖需求分析、设计、编码到测试。通过实例展示了Servlet如何处理用户登录(如`LoginServlet`),JSP负责页面展示(如`login.jsp`和`bookList.jsp`)。应用基于MySQL数据库,包含用户和图书表。登录失败显示错误信息,成功后展示图书列表。部署到Tomcat服务器测试功能。此基础教程为深入Java Web开发奠定了基础。
147 10