Servlet 规范扩展 ------ 监听器接口
1、介绍:
1)一组来自于 Servlet 规范下的接口,共有8个接口。
2)监听器接口需要由开发人员亲自实现,Http服务器提供 jar 包并没有对应的实现类。
3)监听器接口用于监控【作用域对象生命周期变化时刻】以及【作用域对象共享数据变化时刻】。
2、作用域对象:
1)Servlet 规范中,在服务端内存中可以在某些条件下为两个 Servlet 之间提供数据共享方案的对象,被称为【作用域对象】。
2)Servlet 规范下作用域对象:
ServletContext
【全局作用域对象】
HttpSession
【会话作用域对象】
HttpServletRequest
【请求作用域对象】
3、监听器接口实现类开发规范(三步):
1)根据监听的实际情况,选择对应监听器接口进行实现;
2)重写监听器接口声明【监听事件处理方法】;
3)在web.xml
文件将监听器接口实现类注册到 Http 服务器。
4、ServletContextListener 接口:
1)作用:通过接口合法的检测全局作用域对象被初始化时刻以及被销毁时刻。
2)监听事件处理方法:
public void contextInitlized(){...} //全局作用域对象被 Http 服务器初始化(创建)时被调用
public void contextDestory(){...} //全局作用域对象被 Http 服务器销毁时被调用
5、ServletContextAttributeListener 接口:
1)作用:通过这个接口合法的检测全局作用域对象共享数据变化时刻。
2)监听事件处理方法:
public void contextAdd(){...} //在全局作用域对象添加共享数据时,触发
public void contextReplaced(){...} //在全局作用域对象更新共享数据时,触发
public void contextRemove(){...} //在全局作用域对象删除共享数据时,触发
实现监听器接口 --- 优化在线考试管理系统
- 在线考试管理系统(未优化) 源代码:【JavaWeb-Servlet】编程(1)--- JavaSE+MySQL+JDBC+Servlet 实现在线考试管理系统(用户信息管理模块)
- 网站预览:
- 实现查询用户信息的代码块:
package com.burnyouth.controller;
import com.burnyouth.dao.UserDao;
import com.burnyouth.entity.User;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
public class UserFindServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserDao dao = new UserDao();
PrintWriter out = null;
//1、【调用Dao】将查询命令推送到数据库服务器上,得到所有用户信息【List】
Date startTime = new Date();//通过Date类,看看遍历用户信息会消耗多少时间?
List<User> userList = dao.findAll();
Date endTime = new Date();
System.out.println(\"执行遍历用户信息耗费时间:\"+(endTime.getTime()-startTime.getTime())+\"毫秒\");
//2、【调用响应对象】将用户信息结合<table>标签命令以二进制形式写入到响应体
//设置响应头content-type属性
response.setContentType(\"text/html;charset=utf-8\");
out = response.getWriter();
out.print(\"<table border=\'2\'>\");
out.print(\"<tr>\");
out.print(\"<td>用户编号</td>\");
out.print(\"<td>用户姓名</td>\");
out.print(\"<td>用户密码</td>\");
out.print(\"<td>用户性别</td>\");
out.print(\"<td>用户邮箱</td>\");
out.print(\"<td>操作</td>\");
out.print(\"</tr>\");
for (User user : userList) {
out.print(\"<tr>\");
out.print(\"<td>\" + user.getUserId() + \"</td>\");
out.print(\"<td>\" + user.getUserName() + \"</td>\");
out.print(\"<td>******</td>\");
out.print(\"<td>\" + user.getSex() + \"</td>\");
out.print(\"<td>\" + user.getEmail() + \"</td>\");
out.print(\"<td><a href=\'/myWeb/user/delete?userId=\"+user.getUserId()+\"\'>\" +
\"删除用户</a></td>\");
out.print(\"</tr>\");
}
out.print(\"</table>\");
}
}
- 执行多次查询代码,IDEA 控制台:
- 问题描述:
可以看到遍历一次用户信息时长基本稳定在 13 ~ 18 毫秒之间,如果这个管理系统的使用者只有一个人,当然用户的体验是非常好的,但这是不现实的,而当系统同时使用的用户人数达到几万时,有的用户就可能需要等待几分钟的时间,那用户体验感就非常差了,所以我们就需要应用适合的优化方案来提高系统的运行速度,而监听器接口
就刚好是一种优化方案。
通过查阅资料发现,遍历用户信息大部分时间都耗费在了 Connection 通道的建立上,所以我们可以在服务器启动时,就预准备创建几个 Connection 接口,这样在执行遍历用户信息代码的时候,就不需要再创建 Connection 通道了。
- 优化代码:
com.burnyouth.listener.Onelistener(监听器):
package com.burnyouth.listener;
import com.burnyouth.util.JdbcUtil;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Onelistener implements ServletContextListener {
/**
* 在全局作用域对象创建时,创建 10 个 Connection 通道
* @param sce 获取全局作用域对象
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//装10个通道的集合
Map connections = new HashMap();
//获取全局作用域对象
ServletContext application = sce.getServletContext();
for (int i = 0; i < 10; i++) {
try {
Connection connection = JdbcUtil.getConnection();
//将创建好的通道放到集合里,并打上 true 标记(代表此通道为空闲状态)
connections.put(connection, true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//最后将集合上交给全局作用域对象
application.setAttribute(\"connections\", connections);
}
/**
* 全局作用域对象销毁时,同时关闭 10 个 Connection 通道
* @param sce 获取全局作用域对象
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
//获取全局作用域对象
ServletContext application = sce.getServletContext();
//将集合取出来
Map map = (Map) application.getAttribute(\"connections\");
//将集合中的通道一个个地关闭
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
Connection connection = (Connection) it.next();
if (connection != null) {
JdbcUtil.close(connection,null,null);
}
}
}
}
xml (仅展示将监听器注册到服务器的代码):
<listener>
<listener-class>com.burnyouth.listener.Onelistener</listener-class>
</listener>
com.burnyouth.util.JdbcUtil(利用方法重载,添加了新的 getConnection() ,close() ):
package com.burnyouth.util;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import java.sql.*;
import java.util.Iterator;
import java.util.Map;
/**
* JDBC工具类
*/
public class JdbcUtil {
/**
* 工具类的构造方法都应该是私有的
* 因为工具类是需要频繁使用的,所以我们要避免代码的重复书写
* 将工具类方法都设为静态的,再将构造方法私有化(这样想new都new不出来),直接采用类名调用
*/
//静态代码块在类加载时执行,并且只执行一次
static {
try {
Class.forName(\"com.mysql.cj.jdbc.Driver\");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private JdbcUtil() {
}
/**
* 获取数据库连接对象
*
* @return 连接
* @throws SQLException
*/
//因为此方法是被调用的方法,所以出现异常直接上抛就行
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(\"jdbc:mysql://localhost:3306/burning_youth\"
, \"root\", \"888\");
}
/**
* 获取服务器在启动时,就创建好的 Connection 通道
* @param request 网站的请求对象
* @return Map集合中空闲的 Connection 通道
* @throws SQLException
*/
public static Connection getConnection(HttpServletRequest request) throws SQLException {
//获取全局作用域对象:
ServletContext application = request.getServletContext();
//获取集合
Map connections = (Map) application.getAttribute(\"connections\");
Connection connection = null;
//获取集合中空闲状态的 Connection 通道
Iterator it = connections.keySet().iterator();
while (it.hasNext()) {
connection = (Connection) it.next();
//查看通道状态
boolean flag = (boolean) connections.get(connection);
if (flag == true) {
//找到空闲状态的通道后,更改状态
connections.put(connection,false);
//结束循环
break;
}
}
return connection;
}
/**
* 关闭资源
*
* @param connection 连接资源
* @param statement 数据库操作对象
* @param resultSet 结果集
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭资源,注意通道不要关闭,将状态更改为空闲状态即可
* @param connection
* @param statement
* @param resultSet
* @param request 网站的请求对象
*/
public static void close(Connection connection, Statement statement,
ResultSet resultSet,HttpServletRequest request) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//获取全局作用域对象
ServletContext application = request.getServletContext();
//获取集合
Map connections = (Map) application.getAttribute(\"connections\");
//更改通道状态
connections.put(connection,true);
}
}
com.burnyouth.dao.UserDao(仅展示优化后的 findAll() 代码):
public List findAll(HttpServletRequest request) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
List userList = new ArrayList();
try {
//调用新方法
connection = JdbcUtil.getConnection(request);
String sql = \"select * from user\";
ps = connection.prepareStatement(sql);
resultSet = ps.executeQuery();
while (resultSet.next()) {
Integer userId = resultSet.getInt(\"userId\");
String userName = resultSet.getString(\"userName\");
String password = resultSet.getString(\"password\");
String sex = resultSet.getString(\"sex\");
String email = resultSet.getString(\"email\");
User user = new User(userId, userName, password, sex, email);
userList.add(user);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//调用新方法
JdbcUtil.close(connection, ps, resultSet,request);
}
return userList;
}
- 优化效果:效果有点顶了,快10倍不止啊 !!! (❁´◡`❁)
随笔:
1、设计模式六大原则之一的开闭原则,禁止我们修改原来写好的代码,所以当我们优化代码功能时,并没有直接修改原来的方法体,而是在类中通过方法重载机制,写入了优化后的代码。
2、向一个项目中添加功能或者优化功能时,如果涉及到方法之间的调用问题,注意由根到顶的逻辑。
来源:https://www.cnblogs.com/Burning-youth/p/16010651.html
本站部分图文来源于网络,如有侵权请联系删除。