Java学习笔记-Day58 监听器、分页
一、监听器
1、简介
事件发生的时间往往是不确定的,当事件发生的时候需要进行一些处理时,就可以使用监听器。
2、监听器相关的API
监听器相关的API包括 事件类 以及 监听器接口。事件类定义了事件类型,监听器接口定义了监听事件的方法。
2.1、事件类
Servlet API中定义了6种事件类型:
(1)上下文相关的事件
- ServletContextEvent:该类表示上下文事件,当应用上下文对象发生改变,如创建或销毁上下文对象时,将触发上下文事件。
- ServletContextAttributeEvent:该类表示上下文属性事件,当应用上下文的属性改变,如增加、删除、覆盖上下文中的属性时,将触发上下文属性事件。
(2)请求相关的事件
- ServletRequestEvent:该类表示请求事件,当请求对象发生改变,如创建或销毁请求对象时,触发请求事件。
- ServletRequestAttributeEvent:该类表示请求属性事件,当请求中的属性改变,如增加、删除、覆盖请求中的属性时,触发请求属性事件。
(3)会话相关的事件
- HttpSessionEvent:该类表示会话事件,当会话对象发生改变,如创建或销毁会话对象,活化或钝化会话对象时,将触发会话事件。
- HttpSessionBindingEvent:该类表示会话绑定事件,当会话中的属性发生变化时,如增加、删除、覆盖会话中的属性时,将触发会话绑定事件。
2.2、监听器接口
Servlet API中定义了8种监听器接口,用来监听不同的事件类型。
(1)上下文相关的监听器
- ServletContextListener:上下文监听器,监听ServletContextEvent事件。
- ServletContextAttributeListener:上下文属性监听器,用来监听ServletContextAttribute事件。
(2)请求相关的监听器
- ServletRequestListener:请求监听器,监听ServletRequestEvent事件。
- ServletRequestAttributeListener:请求属性监听器,用来监听ServletRequestAttributeEvent事件。
(3)会话相关的监听器
-
HttpSessionListener:会话监听器,监听HttpSessionEvent。当会话对象被创建后或销毁前需要一些自定义处理时,可以用此监听器监听,
-
HttpSessionActivationListener:会话活化监听器,监听HttpSessionEvent事件。会话对象存在于服务器端,只要没有失效,服务器就得分配空间给其使用;为了能够提高使用效率,服务器有内在的活化钝化机制,可以将暂时不使用的会话对象钝化到外存,需要使用时再活化到内存。当活化后或钝化前需要一些自定义处理时,可以使用该监听器。
-
HttpSessionAttributeListener:会话属性监听器,监听HttpSessionAttributeEvent事件。当会话中的属性被添加、删除、替换时,要进行一些自定义处理时,可以使用该监听器,使用时可以用事件对象获取属性的名字等信息。
-
HttpSessionBindingListener:会话绑定监听器,监听HttpSessionAttributeEvent事件。当类实现了HttpSessionBindingListener接口后,该类对象绑定或解除绑定到会话时,就会被该监听器监听。绑定指的是调用setAttribute方法,解除绑定指的是调用removeAttribute方法,或者会话超时、会话失效等。
3、监听器的开发及配置
(1)自定义一个类,并实现相应的监听器接口。
(2)重写监听器接口中的方法。
(3)让监听器生效,需要在web.xml或者注解中进行配置。
- 在web.xml中的配置:
<listener>
<listener-class>com.etc.bs.TestListener</listener-class>
</listener>
- 在自定义类的前一行写注解:
@WebListener
public class OnlineCountListener implements HttpSessionListener {
}
4、Eclispe创建监听器
(1)鼠标右键 -> New -> Listener -> 输入包名和类名 -> Next -> 选择进行监听的事件 ( 例如:HTTP session events 的 Lifecycle ) -> Finsh。
5、案例 (统计在线人数)
servlet中对session的监听有很多接口,功能很灵活,最常用的是监听Session和Attribute。servlet中的session监听和Attribute监听含义有差别,session监听指的不是我们一般所理解的放置一个session或者销毁一个session,这是Attribute监听的功能,因为servlet中放置session的语法是session.setAttribute(“session名”,要放入的对象)。而session监听,监听的是HTTP连接,只要有用户与server连接,就算连接的是一个空白的jsp页面,也会触发session事件,所以此处的session实际上指的是connection,最合适用来统计当前在线人数。
使用监听器 HttpSessionListener,将计数的变量储存在application中,可以通过如下代码获取appliacation。
ServletContext application = se.getSession().getServletContext();
统计在线人数的监听器:
package com.etc.bs.listener;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* 统计在线人数
*
*/
@WebListener
public class OnlineCountListener implements HttpSessionListener {
//统计Session数量
private static int count = 0;
/**
* 无参构造方法
*/
public OnlineCountListener() {
System.out.println("OnlineCountListener构造方法");
}
/**
* @see HttpSessionListener#sessionCreated(HttpSessionEvent)
*/
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session的创建");
++count;
ServletContext application = se.getSession().getServletContext();
application.setAttribute("count", count);
}
/**
* @see HttpSessionListener#sessionDestroyed(HttpSessionEvent)
*/
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session的销毁");
--count;
ServletContext application = se.getSession().getServletContext();
application.setAttribute("count", count);
}
}
二、分页
1、分页的方式
(1)逻辑分页:一次性查询所有数据,在前端代码实现分页功能。
SELECT * FROM users;
(2)物理分页:每次只查询当前页的数据,每次都会向数据库发送一次查询请求。
SELECT * FROM users LIMIT 1,5;"
2、分页的要素
实现分页需要的五个要素:
(1)当前页数(当前第几页)。
(2)每页显示的记录数(每页显示几条数据)。
(3)总记录数(总共多少条记录)。
(4)总页数(总共有几页)。
(5)当前页的数据。
可以将这些要素封装成一个PageData类。
package com.etc.bs.entity;
import java.util.List;
public class PageData<T> {
// 当前页数
private int pageNo;
// 每页的记录数
private int pageSize;
// 总记录数
private int totalCount;
// 总页数
private int totalPage;
// 当前页的数据
private List<T> list;
public int getPageNo() {
return pageNo;
}
public void setPageNo(int pageNo) {
this.pageNo = pageNo;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
/**
* 总页数totalPage的get方法
* @return 返回一个总页数
*/
public int getTotalPage() {
totalPage = totalCount/pageSize;
if((totalCount%pageSize) != 0) {
totalPage = totalPage+1;
}
return totalPage;
}
@Override
public String toString() {
return "PageData [pageNo=" + pageNo + ", pageSize=" + pageSize + ", totalCount=" + totalCount + ", totalPage="
+ totalPage + "]";
}
}
3、增加了分页查询的通用类
package com.etc.bs.utils;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import com.etc.bs.entity.PageData;
/**
* 数据库访问的通用类,其中,实现了增加删除修改通用操作;实现了查询多行记录的通用操作;
*
*
*/
public class DBUtil {
//数据库连接的url
private static final String url = "jdbc:mysql://localhost:3306/blogdb?useSSL=FALSE&serverTimezone=Asia/Shanghai";
//数据库连接的user
private static final String user = "root";
//数据库连接的password
private static final String password = "root";
private DBUtil() {
}
/**
* 建立数据库连接Connection
*
* @return conn 数据库连接
*/
public static Connection getConn() {
Connection conn = null;
try {
// 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 创建数据库连接
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 封装的增加、删除和修改的通用代码(针对任何表)
*
* @param sql sql语句
* @param params sql语句的参数
* @return true 操作成功;false 操作失败
*/
public static boolean exUpdate(String sql, Object... params) {
Connection conn = null;
PreparedStatement pstmt = null;
int n = 0;
try {
conn = getConn();
pstmt = conn.prepareStatement(sql);
// 给空格占位符赋值
setParameters(pstmt, params);
// 执行具体操作,获取结果集
n = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
closeAll(null, pstmt, conn);
}
return n > 0;
}
/**
* 设置PreparedStatement的SQl语句的参数
*
* @param pstmt PreparedStatement对象
* @param params sql语句的参数
* @throws SQLException SQLException异常
*/
private static void setParameters(PreparedStatement pstmt, Object... params) throws SQLException {
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
}
/**
* 封装的查询的通用代码(返回的结果是一个集合)
*
* @param sql Sql语句
* @param cla Class对象
* @param params sql语句的参数
* @return List 集合
*/
public static List exQuery(String sql, Class cla, Object... params) {
List list = new ArrayList();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 获取PreparedStatement对象
conn = getConn();
pstmt = conn.prepareStatement(sql);
// 给空格占位符赋值
setParameters(pstmt, params);
// 发送Sql语句,获得结果集
rs = pstmt.executeQuery();
// 遍历结果集,最初光标指向第一行之前,通过next()方法向下移动一行
while (rs.next()) {
// 将当前这一行的所有列的值赋值给对象的属性
Object obj = convert(rs, cla);
// 将获取的对象obj添加到List集合中
list.add(obj);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(rs, pstmt, conn);
}
return list;
}
/**
* 查询总记录数的方法
* @param sql sql语句
* @param params 参数
* @return int 总记录数
*/
public static int exCountQuery(String sql, Object... params) {
//总记录数
int totalCount = 0;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 获取PreparedStatement对象
conn = getConn();
pstmt = conn.prepareStatement(sql);
// 给空格占位符赋值
setParameters(pstmt, params);
// 发送Sql语句,获得结果集
rs = pstmt.executeQuery();
if (rs.next()) {
// 检索当前行中指定列的值
totalCount = rs.getInt("c");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(rs, pstmt, conn);
}
return totalCount;
}
/**
* 分页查询的方法
* @param sql sql语句
* @param cla 类
* @param pageNo 当前页数
* @param pageSize 当前页的记录数
* @param params 参数
* @return PageData
*/
public static PageData exQueryByPage(String sql, Class cla, int pageNo,int pageSize,Object... params) {
List list = new ArrayList();
// 拼接查询总记录数的sql语句
String totalCountSql = "select count(*) as c from ("+sql+") as t;";
// 调用exCountQuery方法,返回一个总记录数
int totalCount = exCountQuery(totalCountSql, params);
// 计算开始的索引( 索引=(当前页数-1)*每页记录数)
int start = (pageNo-1) * pageSize;
// 拼接查询分页数据的sql语句
sql = sql+" limit "+start+","+pageSize+";";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 获取PreparedStatement对象
conn = getConn();
pstmt = conn.prepareStatement(sql);
// 给空格占位符赋值
setParameters(pstmt, params);
// 发送Sql语句,获得结果集
rs = pstmt.executeQuery();
// 遍历结果集,最初光标指向第一行之前,通过next()方法向下移动一行
while (rs.next()) {
// 将当前这一行的所有列的值赋值给对象的属性
Object obj = convert(rs, cla);
// 将获取的对象obj添加到List集合中
list.add(obj);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(rs, pstmt, conn);
}
PageData pd = new PageData();
//设置当前页数
pd.setPageNo(pageNo);
//设置当前页的记录数
pd.setPageSize(pageSize);
//设置总页数
pd.setTotalCount(totalCount);
//设置当前页的数据
pd.setList(list);
return pd;
}
/**
* 转换方法 将rs读取结果转换对象
*
* @param rs rs对象
* @param cla Class对象
* @return Object对象
*/
private static Object convert(ResultSet rs, Class cla) {
Object obj = null;
try {
obj = cla.newInstance();
ResultSetMetaData rsm = rs.getMetaData();
// getColumnCount:返回此 ResultSet对象中的总列数(从1开始)
for (int i = 1; i <= rsm.getColumnCount(); i++) {
// getColumnLabel:获取指定列名(如果列有别名,则获取列的别名)
String name = rsm.getColumnLabel(i);
Object objvalue = rs.getObject(name);
// setProperty:直接设置对象属性的值,会调用对象的setter方法
BeanUtils.setProperty(obj, name, objvalue);
}
} catch (SQLException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
/**
* 释放资源
*
* @param rs ResultSet对象
* @param pstmt PreparedStatement对象
* @param conn Connection对象
*/
public static void closeAll(ResultSet rs, PreparedStatement pstmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
三、Layui前端框架
1、简介
layui(谐音:类UI) 是一款采用自身模块规范编写的前端 UI 框架,遵循原生 HTML/CSS/JS 的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到 API 的每一处细节都经过精心雕琢,非常适合界面的快速开发。
layui 首个版本发布于 2016 年金秋,区别于那些基于 MVVM 底层的 UI 框架,却并非逆道而行,而是信奉返璞归真之道。准确地说,更多是为后端程序员量身定做,无需涉足各种前端工具的复杂配置,只需面对浏览器本身,让一切你所需要的元素与交互,从这里信手拈来。
layui 兼容正在使用的全部浏览器(IE6/7除外),可作为 PC 端后台系统与前台界面的速成开发方案。
开发文档地址:https://www.layui.com/doc/
2、使用
(1)通过地址 https://www.layui.com/
下载layui-v2.5.7的压缩包,将解压后文件夹里的文件夹 layui 放入Web项目文件的WebContent中。
- layui 的目录结构
├─css //css目录
│ │─modules //模块css目录(一般如果模块相对较大,我们会单独提取,比如下面三个:)
│ │ ├─laydate
│ │ ├─layer
│ │ └─layim
│ └─layui.css //核心样式文件
├─font //字体图标目录
├─images //图片资源目录(目前只有layim和编辑器用到的GIF表情)
│─lay //模块核心目录
│ └─modules //各模块组件
│─layui.js //基础核心库
└─layui.all.js //包含layui.js和所有模块的合并文件
(2)在网页文件(html、JSP)中引入 layui 的css文件和js文件。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- 加入taglib标记 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<c:set var="basepath" scope="page" value="${pageContext.request.contextPath}"></c:set>
<!-- 导入layui框架的layui.css -->
<link href="${basepath}/layui/css/layui.css" rel="stylesheet" media="all">
</head>
</body>
<!-- 导入layui框架的layui.js -->
<script src="${basepath}/layui/layui.js" charset="utf-8"></script>
</body>
</html>
(3)使用 layui 模块。