JavaWeb-Servlet笔记--- 监听器接口;应用监听器接口 优化在线考试管理系统
Posted 猿头猿脑的王狗蛋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb-Servlet笔记--- 监听器接口;应用监听器接口 优化在线考试管理系统相关的知识,希望对你有一定的参考价值。
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、向一个项目中添加功能或者优化功能时,如果涉及到方法之间的调用问题,注意由根到顶的逻辑。
以上是关于JavaWeb-Servlet笔记--- 监听器接口;应用监听器接口 优化在线考试管理系统的主要内容,如果未能解决你的问题,请参考以下文章