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

JSP 網頁計數器實作:使用 Filter 實現訪客統計與持久化儲存

     編輯:關於JSP

網頁計數器是經典的 Web 應用功能,用於記錄網站訪問人次。本文介紹如何利用 Servlet Filter(過濾器)實現一個高效、自動持久化的訪客計數器,涵蓋初始化讀取、請求攔截計數、銷毀寫入檔案的完整生命週期管理。

核心架構:Filter 生命週期管理計數器

選用 javax.servlet.Filter 而非 Servlet 或 Listener 的優勢在於:

  • 自動啟動/關閉:Web 容器啟動時自動調用 init(),關閉時自動調用 destroy(),無需手動觸發。
  • 攔截特定路徑:可在 doFilter() 中精確判斷請求路徑(如 /login.action),只統計有效業務訪問。
  • 減少 I/O 操作:計數器放在 ServletContext 記憶體中,僅在啟動讀取與關閉寫入時存取磁碟,效能極佳。

完整實作代碼

EncodingFilter.java

public class EncodingFilter implements Filter {
    private String charset = "UTF-8";
    private ServletContext context;
    private String counterFilePath;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 1. 讀取初始化參數
        this.charset = filterConfig.getInitParameter("encoding");
        this.context = filterConfig.getServletContext();
        
        // 2. 確定計數器檔案路徑(建議放在 WEB-INF 下,避免外部直接訪問)
        String realPath = context.getRealPath("/WEB-INF/counter.txt");
        this.counterFilePath = realPath;

        // 3. 啟動時讀取歷史計數
        File file = new File(counterFilePath);
        String count = "0";
        if (file.exists()) {
            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                count = reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // 首次啟動,建立檔案並寫入 0
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
                writer.write("0");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        context.setAttribute("visitCount", count);
        System.out.println("計數器初始化完成,當前計數:" + count);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestPath = httpRequest.getServletPath();

        // 僅統計登入動作(或首頁等核心路徑),避免靜態資源、AJAX 輪詢干擾
        if (requestPath.endsWith("/login.action") || requestPath.equals("/")) {
            // 從 ServletContext 取得計數並加 1
            Integer current = Integer.parseInt(context.getAttribute("visitCount").toString());
            context.setAttribute("visitCount", String.valueOf(current + 1));
        }

        // 統一設定編碼
        request.setCharacterEncoding(charset);
        response.setCharacterEncoding(charset);
        
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 關閉時將最新計數寫入檔案
        String count = (String) context.getAttribute("visitCount");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(counterFilePath))) {
            writer.write(count);
            System.out.println("計數器持久化完成,最終計數:" + count);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

web.xml 註冊配置

<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.example.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!-- 容器啟動時即載入 -->
    <load-on-startup>1</load-on-startup>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

進階優化建議

  • 原子性操作:高併發場景下,建議使用 AtomicInteger 或資料庫序列替代 ServletContext 字串轉換。
  • 分散式部署:多節點環境需改用 Redis、資料庫或共享儲存實現全域計數。
  • 排除靜態資源:在 doFilter 中過濾 .css、.js、.jpg 等靜態請求,避免計數膨脹。
  • IP 去重:結合 Session 或 Cookie 實現「獨立訪客(UV)」統計,而非單純 PV。

此方案結構清晰、擴展性強,適合中小型專案快速接入訪客統計功能。

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