首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Servlet 監聽器實現網站在線人數統計完整示例

     編輯:Java 相關知識

前言

在 Web 應用開發中,統計當前在線用戶人數是一項常見需求。本文將詳細介紹如何利用 Java Servlet 規範提供的三大核心監聽器 —— ServletContextListener、HttpSessionAttributeListener 與 HttpSessionListener —— 協同實現一個輕量級的在線人數統計功能。示例代碼結構清晰,適合初學者參考,也可直接應用於實際項目中。

核心設計思路

  1. 應用啟動時初始化容器:通過 ServletContextListener.contextInitialized() 在 ServletContext 中創建一個線程安全的 List<String>,用於存放所有在線用戶的用戶名。
  2. 用戶登入時註冊:利用 HttpSessionAttributeListener.attributeAdded() 監聽 Session 屬性新增事件,當檢測到屬性名為 username 時,將該用戶名加入全局列表。
  3. 用戶登出/會話銷毀時移除:通過 HttpSessionListener.sessionDestroyed() 在會話銷毀時,從全局列表中移除對應用戶名。

完整實現代碼

1. 統一監聽器實現類

package com.web.servlet;

import java.util.LinkedList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 在線人數統計監聽器
 * 同時實現三大監聽器接口,集中管理用戶上下線邏輯
 */
public class OnlineListener implements ServletContextListener,
        HttpSessionAttributeListener, HttpSessionListener {

    private ServletContext application = null;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        this.application = sce.getServletContext();
        // 初始化線程安全的用戶名列表
        this.application.setAttribute("online", new LinkedList<String>());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 應用銷毀時可執行清理工作
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        // 只處理 username 屬性的新增
        if ("username".equals(event.getName())) {
            @SuppressWarnings("unchecked")
            List<String> online = (List<String>) application.getAttribute("online");
            synchronized (online) {
                online.add((String) event.getValue());
                application.setAttribute("online", online);
            }
        }
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
        // 可選:處理屬性移除邏輯
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
        // 可選:處理屬性替換邏輯
    }

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        // 會話創建時不做處理,待登入時再統計
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        @SuppressWarnings("unchecked")
        List<String> online = (List<String>) application.getAttribute("online");
        String username = (String) event.getSession().getAttribute("username");
        if (username != null) {
            synchronized (online) {
                online.remove(username);
                application.setAttribute("online", online);
            }
        }
    }
}

2. web.xml 註冊監聽器

<!-- 註冊在線統計監聽器 -->
<listener>
    <listener-class>com.web.servlet.OnlineListener</listener-class>
</listener>

3. 登入 Servlet

package com.web.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        String username = req.getParameter("username");

        if (username != null && !username.trim().isEmpty()) {
            // 存入 Session,自動觸發 HttpSessionAttributeListener
            req.getSession().setAttribute("username", username);
        }

        // 從應用上下文獲取在線列表並輸出
        @SuppressWarnings("unchecked")
        List<String> online = (List<String>) getServletContext().getAttribute("online");

        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("<html><head><title>用戶列表</title></head><body>");
        out.println("當前用戶:" + username + "<hr/><h3>在線用戶列表</h3>");

        int size = (online == null) ? 0 : online.size();
        for (int i = 0; i < size; i++) {
            out.println((i + 1) + ". " + online.get(i) + "<br/>");
        }

        // 使用 encodeURL 保證會話追蹤
        String logoutUrl = resp.encodeURL("logout");
        out.println("<hr/><a href='" + logoutUrl + "'>登出</a>");
        out.println("</body></html>");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doPost(req, resp);
    }
}

4. 登出 Servlet

package com.web.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LogoutServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 銷毀會話,觸發 HttpSessionListener.sessionDestroyed()
        req.getSession().invalidate();

        @SuppressWarnings("unchecked")
        List<String> online = (List<String>) getServletContext().getAttribute("online");

        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("<html><head><title>用戶列表</title></head><body>");
        out.println("<h3>在線用戶列表</h3>");

        int size = (online == null) ? 0 : online.size();
        for (int i = 0; i < size; i++) {
            out.println((i + 1) + ". " + online.get(i) + "<br/>");
        }
        out.println("<hr/><a href='index.html'>首頁</a></body></html>");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doPost(req, resp);
    }
}

5. 登入頁面 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用戶登入</title>
</head>
<body>
    <form action="login" method="post">
        用戶名:<input type="text" name="username" />
        <input type="submit" value="登入" />
    </form>
</body>
</html>

部署與驗證

  1. 將項目打包為 WAR 並部署至 Tomcat。
  2. 啟動服務器,瀏覽器訪問 https://localhost:8080/你的項目名/。
  3. 多開幾個瀏覽器視窗或使用無痕模式分別登入不同用戶名,觀察在線列表即時更新。

關鍵點總結

  • 三大監聽器分工明確:ServletContextListener 負責全局容器生命週期,HttpSessionAttributeListener 捕捉登入動作,HttpSessionListener 處理會話結束。
  • 使用 LinkedList 並配合 synchronized 保證併發安全。
  • 登出時調用 session.invalidate() 而非手動移除屬性,確保監聽器邏輯被正確觸發。
  • 輸出鏈接時務必使用 response.encodeURL() 支持 Cookie 被禁用場景下的會話追蹤。

本示例採用最原生的 Servlet API 實現,無第三方依賴,適合理解 Web 容器監聽機制的基礎教學與輕量級項目直接使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved