【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对所有服务的控制一样,更重要的是思想,而组件只是思想的落地。有了思想我们可以应用再更多其它地方。

相关文章
|
2天前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
10 4
|
1天前
|
Java 持续交付 项目管理
使用Maven进行项目管理:提高Java Web开发的效率
Maven 是一款强大的项目管理和构建自动化工具,广泛应用于Java社区。它通过依赖管理、构建生命周期管理、插件机制和多模块项目支持等功能,简化了项目的构建过程,提高了开发效率。本文将介绍Maven的核心功能及其在Java Web开发中的应用。
11 0
WK
|
7天前
|
安全 Java 编译器
C++和Java哪个更适合开发web网站
在Web开发领域,C++和Java各具优势。C++以其高性能、低级控制和跨平台性著称,适用于需要高吞吐量和低延迟的场景,如实时交易系统和在线游戏服务器。Java则凭借其跨平台性、丰富的生态系统和强大的安全性,广泛应用于企业级Web开发,如企业管理系统和电子商务平台。选择时需根据项目需求和技术储备综合考虑。
WK
10 0
|
1月前
|
前端开发 Java API
JAVA Web 服务及底层框架原理
【10月更文挑战第1天】Java Web 服务是基于 Java 编程语言用于开发分布式网络应用程序的一种技术。它通常运行在 Web 服务器上,并通过 HTTP 协议与客户端进行通信。
21 1
|
28天前
|
Java 应用服务中间件 Spring
【终极解决方案】Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
【终极解决方案】Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
21 0
|
3月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
32 3
|
3月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
31 1
|
3月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
基于Servlet和JSP的Java Web应用开发指南
41 0
|
3月前
|
前端开发 安全 Java
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
27 0
|
5月前
|
自然语言处理 前端开发 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开发基础的关键。
100 12