统计在线账户信息的开发过程大致分为以下几步:
一、开发RequestListener类,并实现ServletRequestListener接口,用于监听每个用户的请求信息。
二、开发OnlineListener类,并实现ServletRequestContext接口,用于定期检查在线记录,并删除长期没有请求的记录。该类是一个后台线程,随着程序的启动而启动
三、DAO层
四、web.xml文件的配置
五、开发JSP,显示在线用户信息
下面开始程序的开发:
一、开发RequestListener类,并实现ServletRequestListener接口,当用户请求到达时,会调用ServletRequestListener接口的requestInitialized方法,当用户断开请求时,会调用ServletRequestListener接口的requestDestroyed方法。RequestListener类主要用于记录访问用户的sessionId,用户状态,用户IP,访问的时间及请求的页面,代码如下:
public class RequestListener implements ServletRequestListener {
// 当用户请求到达,被初始化时触发该方法
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
HttpSession session = request.getSession();
// 获得sessionId
String sessionId = session.getId();
// 访问的页面
String page = request.getRequestURI();
// 获得用户的IP
String ip = request.getRemoteAddr();
String user = (String) session.getAttribute("user");
user = (user == null) ? "游客" : user;
DbDao db = new DbDao("oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@127.0.0.1:1521:ORCAL", "scott", "sa");
try {
// 这里的SQL语句写成select *,把要查询的字段列出来才可以,不然会出现"对只读结果集的无效操作updateString"的错误信息
ResultSet rs = db.query("select session_id,username,user_ip,access_res,last_access from online_info where session_id=? ", true, sessionId);
// 如果有记录,则更新,否则插入数据库
if (rs.next()) {
rs.updateString(4, page);
rs.updateLong(5, System.currentTimeMillis());
rs.updateRow();
rs.close();
} else {
db.insert("insert into online_info values(?,?,?,?,?)", sessionId, user, ip, page, System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 当用户退出被销毁时触发该方法
public void requestDestroyed(ServletRequestEvent sre) {
}
}
二、开发OnlineListener对象,实现ServletRequestContext接口,当应用程序开启时,会调用ServletRequestContext接口的contextInitialized方法,当应用程序关闭时,会调用ServletRequestContext接口的contextDestroyed方法。OnlineListener对象用于定期检查在线记录,并删除长期没有请求的记录。该类是一个后台线程,随着程序的启动而启动,代码如下:
public class OnlineListener implements ServletContextListener {
public static final int MAX_MILLIS=1*60*1000;
//程序起动时触发该方法
public void contextInitialized(ServletContextEvent arg0) {
new Timer(1000*5, new ActionListener(){
public void actionPerformed(ActionEvent e) {
try {
DbDao dd=new DbDao("oracle.jdbc.OracleDriver","jdbc:oracle:thin:@127.0.0.1:1521:ORCAL","scott","sa");
ResultSet rs=dd.query("select * from online_info", false);
StringBuilder beRemove=new StringBuilder("(");
while(rs.next()){
//如果当前距离上次访问时间超过了指定时间
if((System.currentTimeMillis()-rs.getLong(5))>MAX_MILLIS){
//将要被删除的SessionID添加进来
beRemove.append("'");
beRemove.append(rs.getString(1));
beRemove.append("' , ");
}
}
//如果有要删除的记录
if(beRemove.length()>3){
beRemove.setLength(beRemove.length()-3);
beRemove.append(")");
dd.modify("delete from online_info where session_id in "+beRemove.toString());
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}).start();
}
//程序关闭时触发该方法
public void contextDestroyed(ServletContextEvent arg0) {
}
}
三、DAO层代码如下:
public class DbDao {
private String driver;
private String uri;
private String userName;
private String password;
//这里省略getter和setter方法
private Connection con = null;
private PreparedStatement pstmt = null;
private ResultSet rs = null;
public DbDao() {
}
public DbDao(String driver, String uri, String userName, String password) {
this.driver = driver;
this.uri = uri;
this.userName = userName;
this.password = password;
}
/**
* @param sql
* sql语句
* @param updatable
* 是否可更新
* @param args
* 可变参数
* @return ResultSet
* @throws Exception
*/
public ResultSet query(String sql, boolean updatable, Object... args) throws Exception {
PreparedStatement pstmt = null;
if (updatable) {
// 创建可更新的prepareStatement
// TYPE_SCROLL_SENSITIVE:生成的ResultSet对象可以定位信息,可向前向后访问,如果用户执行完后删除一条记录,那么也从ResultSet中删除
// CONCUR_UPDATABLE:指定可以更新ResultSet
pstmt = getCon().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
pstmt = getCon().prepareStatement(sql);
}
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}
rs = pstmt.executeQuery();
return rs;
}
/**
*
* @param sql
* sql语句
* @param args
* 可变参数
* @return boolean
* @throws Exception
*/
public boolean insert(String sql, Object... args) throws Exception {
pstmt = getCon().prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}
if (pstmt.executeUpdate() != 1) {
return false;
}
return true;
}
public Connection getCon() throws Exception {
if (con == null) {
Class.forName(driver);
con = DriverManager.getConnection(uri, userName, password);
}
return con;
}
public void modify(String sql, Object... args) throws SQLException, Exception {
pstmt = getCon().prepareStatement(sql);
pstmt.executeUpdate();
}
}
四、在web.xml文件中配置listener
<listener>
<listener-class>listener.RequestListener</listener-class>
</listener>
<listener>
<listener-class>listener.OnlineListener</listener-class>
</listener>
五、JSP的开发,为了简单,这里直接在jsp中写Java代码了,代码如下:
<%@page contentType="text/html;charset=utf-8" errorPage="" %>
<%@page import="java.sql.*,listener.*" %>
<html>
<head>
<title>统计在线用户信息</title>
</head>
<body>
<h2>当前在线用户信息</h2>
<table style="border:solid 1px black; width:700px;">
<tr><td>sessionId</td><td>用户类型</td><td>用户IP地址</td><td>请求页面</td></tr>
<% DbDao dd=new DbDao("oracle.jdbc.OracleDriver","jdbc:oracle:thin:@127.0.0.1:1521:ORCAL","scott","sa");
ResultSet rs=dd.query("select * from online_info",false);
while(rs.next()){%>
<tr style="border:solid 1px black;">
<td><%=rs.getString(1) %></td>
<td><%=rs.getString(2) %></td>
<td><%=rs.getString(3) %></td>
<td><%=rs.getString(4) %></td>
</tr>
<% }%>
</table>
</body>
</html>
到此,统计在线用户信息的代码就已经开发完了,只需在地址栏中直接访问JSP,就可以查看到在线用户的信息
注:此文章部分代码功能摘至《轻量级JavaEE企业应用实践》李刚 著