监听器Listener+百度Echarts实现请求流量分析统计
之前学习的过滤器,过滤器是针对于请求过滤。而今天我们要学习的监听器,就像汽车的自动刹停技术,监听汽车与前方物体的距离,从而采取措施。JavaWeb中的监听器也是如此,监听事物变化,从而采取措施。
- 监听器(Listener)是J2EE Servlet模块下的组件
- Listener的作用对Web应用对象的行为进行监控
- 通过Listener监听Web应用对象功能状态的变化,自动触发指定的功能代码
三种监听对象
- ServletContext-对全局ServletContext及其属性进行监听
- HttpSession-对用户会话及其属性操作进行监听
- ServletRequest-对请求及属性操作进行监听
过滤器与监听器的区别
- 过滤器(Filter)的职责是对URL进行过滤拦截,是主动的执行
- 监听器(Listener)的职责是对Web对象进行监听,是被动触发
开发监听器三要素
- 实现XxxListener接口,不同接口对应不同监听对象
- 实现每个接口中独有的方法,实现触发监听的后续操作
- 在web.xml中配置
<listener>
使监听器生效
案例:实现ServletContextListener接口演示一个简单监听器案例
FirstListener.java
package listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
//注解形式
//@WebListener
public class FirstListener implements ServletContextListener{
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
System.out.print("ServletContext已销毁");
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
System.out.print("ServletContext已初始化");
}
}
推荐使用web.xml配置的形式而不是注解引入监听器,因为listener往往是应用程序全局的监听操作,对于这种全局的应用方式最好使用web.xml配置的方式
web.xml
<listener>
<listener-class>listener.FirstListener</listener-class>
</listener>
内置对象监听器
监听器的实现需要实现监听器接口,每种监听器接口对应不同的监听器对象。6种常用监听接口:
内置对象监听器接口:
ServletContextListener - 监听ServletContext对象创建、销毁等操作
HttpSessionListener - 监听HttpSession对象创建、销毁等操作
ServletRequestListener - 监听HttpServletRequest对象创建、销毁
HelloServlet.java
package listener;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class HelloServlet
*/
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().println("Hello World");
//在request这3个对象中各存放3个自定义属性,方便属性监听器的使用
request.getServletContext().setAttribute("sc-attr1","sc-attr-value1");
request.getSession().setAttribute("session-attr1", "session-attr-value1");
request.setAttribute("request-attr1", "request-attr-value1");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
WebListener.java
package listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class WebListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent arg0) {
// TODO Auto-generated method stub
System.out.println("HttpServletRequest已被销毁");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest)sre.getServletRequest();
System.out.println("HttpServletRequest已被创建,URI:"+request.getRequestURI());
}
@Override
public void sessionCreated(HttpSessionEvent se) {
// TODO Auto-generated method stub
HttpSession session = se.getSession();
System.out.println("Session已被创建,SessionId:"+session.getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
System.out.println("Session已被销毁");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
System.out.println("ServletContext已销毁");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
System.out.println("ServletContext已初始化");
}
}
web.xml完成配置即可
web应用程序执行过程
发起一个请求时 tomcat处理的过程:Web应用启动时,ServletContext被初始化,启动成功后访问地址时,一个HttpServletRequest对象被创建。同时因为是一个新的浏览器窗口,所以tomcat会创建一个session对象,网页处理完成后HttpServletRequest被销毁。
第二次请求(当浏览器窗口刷新后),session 并没有被创建,因为session id 已存在,通过确认session id 的存在,所以并不会创建新的session。此时,HttpServletRequest 又经历了一次创建与销毁的过程。
当浏览器关闭重新打开一个新的窗口,再次访问这个网址,这时一个新的session被创建。原因是新的浏览器并没有包含任何session id,所以由新的浏览器窗口向tomcat发送请求后,会为其创建一个对应的session,原有的session并不会消失,只是原有的sessionid的凭证不存在了,30分钟后自然过期,或程序代码人为关闭。
当关闭应用时,ServletContext 才被销毁。
属性监听接口(用的不多,了解)
- ServletContextAttributeListener - 监听全局属性操作
- HttpSessionAttributeListener - 监听用户会话属性操作
- ServletRequestAttributeListener - 监听请求属性操作
WebAttributeListener.java
public class WebAttributeListener implements ServletContextAttributeListener,HttpSessionAttributeListener ,ServletRequestAttributeListener{
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
System.out.println("ServletContext新增属性:" + event.getName() + "->" + event.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
}
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
System.out.println("HttpSession新增属性:" + event.getName() + "->" + event.getValue());
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("Request新增属性:" + srae.getName() + "->" + srae.getValue());
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
}
}
监听器的应用场景
案例:请求流量分析
echarts:https://echarts.apache.org/zh/tutorial.html
用到了echarts.min.js、jquery-3.6.0.min.js、fastjson-1.2.53.jar
监听器RequestTotalListener.java
package total;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
public class RequestTotalListener implements ServletContextListener, ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// TODO Auto-generated method stub
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
// TODO Auto-generated method stub
//将"/rt"这个url排除在外
HttpServletRequest request = (HttpServletRequest)sre.getServletRequest();
String url = request.getRequestURL().toString();
if(url.endsWith("/rt")==true) {
return;
}
//先取出存放在全局对象中的时间和次数数据
//TimeList: 10:02 10:03 10:04 10:05
//ValueList: 5 7 10 2
List<String> timeList = (List)sre.getServletContext().getAttribute("timeList");
List<Integer> valueList = (List)sre.getServletContext().getAttribute("valueList");
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
String time = sdf.format(date);
if(timeList.indexOf(time)==-1) {
timeList.add(time);
valueList.add(1);
sre.getServletContext().setAttribute("timeList", timeList);
sre.getServletContext().setAttribute("valueList", valueList);
}else {
int index = timeList.indexOf(time);
int value = valueList.get(index);
valueList.set(index, value+1);
sre.getServletContext().setAttribute("valueList", valueList);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
List timeList = new ArrayList();
List valueList = new ArrayList();
sce.getServletContext().setAttribute("timeList", timeList);
sce.getServletContext().setAttribute("valueList", valueList);
}
}
RequestTotalServlet.java进行与前台的数据通信
package total;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
/**
* Servlet implementation class RequestTotalServlet
*/
@WebServlet("/rt")
public class RequestTotalServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public RequestTotalServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
ServletContext context = request.getServletContext();
List<String> timeList = (List)context.getAttribute("timeList");
List<Integer> valueList = (List)context.getAttribute("valueList");
response.setContentType("text/html;charset=utf-8");
// response.getWriter().println(timeList.toString()+"<br/>");
// response.getWriter().println(valueList.toString());
Map result = new HashMap();
result.put("timeList",timeList);
result.put("valueList",valueList);
String json = JSON.toJSONString(result);
response.getWriter().println(json);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
total.html图表展示页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/echarts.min.js"></script>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
//编写js定时器不断请求数据实现同步
function showChart(){
$.ajax({
url:"./rt",
type:"get",
"dataType":"json",
success:function(json){
console.log(json.timeList);
console.log(json.valueList);
// 基于准备好的dom,初始 化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: '请求流量统计分析'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: json.timeList
},
yAxis: {},
series: [{
name: '访问量',
type: 'line',
data: json.valueList
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
})
}
window.setInterval("showChart()",1000); //每秒刷新
</script>
</body>
</html>
test1.html写一些网站测试页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>test1</h1>
</body>
</html>
案例:静态数据预处理
对于一些长期不会改变的数据,可以在服务器每次启动的时候利用上下文监听器在全局属性中写入。可以避免每次加载页面时都要从数据库获取数据
使用jstl两个jar包taglibs-standard-impl-1.2.5.jar和taglibs-standard-spec-1.2.5.jar
在程序启动时添加全局静态频道和url
静态数据监听器StaticDataListener.java
public class StaticDataListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
List list = new ArrayList();
list.add(new Channel("Java" , "my Java"));
list.add(new Channel("UI" , "my UI"));
list.add(new Channel("Video" , "my Video"));
sce.getServletContext().setAttribute("channelList", list);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
Channel.java频道类
public class Channel {
private String channelName;
private String url;
public Channel(String channelName, String url) {
super();
this.channelName = channelName;
this.url = url;
}
public String getChannelName() {
return channelName;
}
public void setChannelName(String channelName) {
this.channelName = channelName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入jstl -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:forEach items="${applicationScope.channelList }" var="c">
<a href="${c.url }">${c.channelName }</a> |
</c:forEach>
<hr/>
</body>
</html>