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()...    //在全局作用域对象删除共享数据时,触发

 

 实现监听器接口 --- 优化在线考试管理系统


 

  • 网站预览:

 

  • 实现查询用户信息的代码块:
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笔记--- 监听器接口;应用监听器接口 优化在线考试管理系统的主要内容,如果未能解决你的问题,请参考以下文章

笔记1

记一次JavaWeb-Servlet排错过程

JavaWeb-Servlet

javaWeb-Servlet

JavaWeb-servlet

JavaWeb-servlet