整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          使用網頁上的數字進行計算并回寫的js案例整理

          事有一個需求是,先讀取網頁中的數字,然后進行計算后再寫到網頁上。解決的方法是,得先用正確的id來標識這兩個數字的元素。比如:<span id="sales">500</span> ,就是把500進行標識起來了。

          然后把數字讀出來。在JavaScript中,`document.getElementById`返回的是一個元素對象,而不是一個數字。這里需要獲取元素的值才能進行計算。

          計算后,結果會回寫在 <span id="result"></span> 標識內。

          如果計算回寫之后,沒有數字,請檢查下js代碼塊是否放在最后。

          <!DOCTYPE html>
          <html>
          <head>
            <title>計算結果</title>
          </head>
          <body>
            <span id="inventory">100</span>  <!-- 今年社會日均庫存 -->
            <span id="sales">500</span>  <!-- 今年以來社會銷量 -->
            <span id="result"></span>
            <script>
              // 獲取頁面上的元素
              var inventoryElement = document.getElementById("inventory");
              var salesElement = document.getElementById("sales");
              var resultElement = document.getElementById("result");
              // 獲取元素的值
              var inventory = parseInt(inventoryElement.textContent);
              var sales = parseInt(salesElement.textContent);
              // 計算結果
              var result = inventory / sales;
              // 在頁面上顯示計算結果
              resultElement.textContent = result.toFixed(2); // 保留兩位小數
            </script>
          </body>
          </html>

          關注我,我們將一起探討如何將技術與業務有機結合。不必成為專家,只要具備基本的印象和理解,便能把握業務與技術的融合,實現二者的平衡發展。在這個過程中,我們將共同尋找如何在促進二者融合發揮最大價值的同時,確保技術與業務的協調一致。

          .背景

          頁面停留時間(Time on Page)簡稱 Tp,是網站分析中很常見的一個指標,用于反映用戶在某些頁面上停留時間的長短,傳統的Tp統計方法會存在一定的統計盲區,比如無法監控單頁應用,沒有考慮用戶切換Tab、最小化窗口等操作場景。 基于上述背景,重新調研和實現了精確統計頁面停留時長的方案,需要 兼容單頁應用和多頁應用,并且不耦合或入侵業務代碼。

          2.分析

          我們可以把一個頁面生命周期抽象為三個動作: 「進入」、「活躍狀態切換」、「離開」

          如下圖,計算頁面停留時長既如何監控這三個動作,然后在對應觸發的事件中記錄時間戳,比如要統計活躍停留時長就把 active 區間相加即可,要統計總時長既 tn -t0 。

          2.1 如何監聽頁面的進入和離開?

          對于常規頁面的 首次加載、頁面關閉、刷新 等操作都可以通過 window.onload 和 window.onbeforeunload 事件來監聽頁面進入和離開,瀏覽器前進后退可以通過 pageshow 和 pagehide 處理。

          對于單頁應用內部的跳轉可以轉化為兩個問題:1.監聽路由變化,2.判斷變化的URL是否為不同頁面 。

          2.1.1 監聽路由變化

          目前主流的單頁應用大部分都是基于 browserHistory (history api) 或者 hashHistory 來做路由處理,我們可以通過監聽路由變化來判斷頁面是否有可能切換。注意是有可能切換,因為URL發生變化不代表頁面一定切換,具體的路由配置是由業務決定的(既URL和頁面的匹配規則)。

          browserHistory

          路由的變化本質都會調用 History.pushState() 或 History.replaceState() ,能監聽到這兩個事件就能知道。通過 popstate 事件能解決一半問題,因為 popstate 只會在瀏覽器前進后退的時候觸發,當調用 history.pushState() or history.replaceState() 的時候并不會觸發。

          The popstate event is fired when the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event's state property contains a copy of the history entry's state object.

          Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling。history.back() or history.forward() in JavaScript).

          這里需要通過猴子補丁(Monkeypatch)解決,運行時重寫 history.pushState 和 history.replaceState 方法:

          let _wr = function (type) {
           let orig = window.history[type]
           return function () {
           let rv = orig.apply(this, arguments)
           let e = new Event(type.toLowerCase())
           e.arguments = arguments
           window.dispatchEvent(e)
           return rv
           }
          }
          window.history.pushState = _wr('pushState')
          window.history.replaceState = _wr('replaceState')
          window.addEventListener('pushstate', function (event) {})
          window.addEventListener('replacestate', function (event) {})
          

          hashHistory

          hashHistory 的實現是基于 hash 的變化,hash 的變化可以通過 hashchange 來監聽

          2.1.2 判斷URL是否為不同頁面

          問題本質是怎么定義一個頁面,這里我們無法自動獲取,因為不同業務場景定義不同,需要業務方在初始化的時候配置 rules 參數,默認不傳入 rules 的情況取 location.pathname 為 key,key 不相同則判斷為不同的頁面,配置的語法:

          new Tracer({
           rules: [
           { path: '/index' },
           { path: '/detail/:id' },
           { path: '/user', query: {tab: 'profile'} }
           ]
          )
          

          對于頁面進入和離開相關事件整理

          2.2 如何監聽頁面活躍狀態切換?

          可以通過 Page Visibility API 以及在 window 上聲明 onblur/onfocus 事件來處理。

          2.2.1 Page Visibility API

          一個網頁的可見狀態可以通過 Page Visibility API 獲取,比如當用戶 切換瀏覽器Tab、最小化窗口、電腦睡眠 的時候,系統API會派發一個當前頁面可見狀態變化的 visibilitychange 事件,然后在事件綁定函數中通過 document.hidden 或者 document.visibilityState 讀取當前狀態。

          document.addEventListener('visibilitychange', function (event) {
           console.log(document.hidden, document.visibilityState)
          })
          

          2.2.2 onblur/onfocus

          2.3 什么時機上報數據?

          2.3.1 頁面離開時上報

          對于頁面刷新或者關閉窗口觸發的操作可能會造成數據丟失

          2.3.2 下次打開頁面時上報

          會丟失歷史訪問記錄中的最后一個頁面數據

          目前采用的方案2,對于單頁內部跳轉是即時上報,對于單頁/多頁應用觸發 window.onbeforeunload 事件的時候會把當前頁面數據暫存在 localStorage 中,當用戶下次進入頁面的時候會把暫存數據上報。有個細節問題,如果用戶下次打開頁面是在第二天,對于統計當天的活躍時長會有一定的誤差,所以在數據上報的同時會把該條數據的頁面進入時間/離開時間帶上。

          3.設計

          3.1 UML類關系圖

          Tracer

          核心類,用來實例化一個監控,對原生事件和自定義事件的封裝,監聽 enter activechange exit 事件來操作當前 Page 實例。

          P.S. 取名來自暴雪旗下游戲守望先鋒英雄獵空(Tracer),直譯為:追蹤者。

          Page

          頁面的抽象類,用來實例化一個頁面,封裝了 enter exit active inactive 等操作,內部通過 state 屬性來維護當前頁面狀態。

          3.2 事件派發關系圖

          4.兼容性

          Desktop

          Mobile

          5.思考

          對于頁面停留時長的定義可能在不同場景會有差異,比如內部業務系統或者OA系統,產品可能更關心用戶在頁面的活躍時長;而對于資訊類型的產品,頁面可見時長會更有價值。單一的數據對業務分析是有限的,所以在具體的代碼實過程中我們會把停留時長分三個指標,這樣能更好的幫助產品/運營分析。

          active 頁面活躍時長

          visible 頁面可見時長 //僅支持Desktop

          duration 頁面總停留時長

          6.TODO

          移動端的兼容性目前還沒完全覆蓋;

          對于頁面的配置目前還不夠靈活,考慮支持 react-router / vue-router 的配置;

          byted-cg-tracer 待封裝;開發中

          7.參考

          https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onhashchange

          https://developer.mozilla.org/en-US/docs/Web/Events/popstate

          https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API

          https://stackoverflow.com/questions/4570093/how-to-get-notified-about-changes-of-the-history-via-history-pushstate

          計用戶在線人數

          在統計用戶在人數的時候,我們用到了監聽器,監聽器大致分為以下三種:

          1. ServletRequestListener:用于監聽請求的監聽接口
          2. HttpSessionListener:用于監聽會話的監聽接口
          3. ServletContextListener:用于監聽應用的回話接口

          錯誤的統計辦法

          監聽Request域

          這種統計辦法是錯誤的認為每次刷新頁面后進行進行一次的count++運算

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
              private ServletContext sc;
              private Integer count;
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //獲取全局域
                  sc = sre.getServletContext();
                  //將count從全局域中獲取出來
                  count = (Integer) sc.getAttribute("count");
                  System.out.println(count);
                  count++;
                  System.out.println(count);
                  sc.setAttribute("count",count);
              }
          }
          
          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創建
              public void contextInitialized(ServletContextEvent sce) {
                  Integer count = 0;
                  //獲取全局域
                  sc = sce.getServletContext();
                  //將count放入到全局域中
                  sc.setAttribute("count",count);
              }
          }
          
          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.count} customer to visit. </h1></center>
            </body>
          </html>
          

          這種錯誤地做法導致的是每刷新一次頁面 就會導致count進行累加操作,最終產生錯誤的在線人數,所以此時想到不應該監聽Request域,而應該監聽Session域。

          監聽Session域

          在第二次監聽Session域之后,發現每次刷新頁面后不改變count但是在啟動不同的瀏覽器后count++會實現,但是,這樣做并不是我們要統計的在線人數,所以此種做法錯誤。由于代碼只是將原來寫在Request監聽器中的代碼轉移到Session監聽器中,所以其他沒變的代碼將不重復。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private Integer count;
          
              @Override
              //當對話產生時激活此方法
              public void sessionCreated(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();
                  count = (Integer) sc.getAttribute("count");
                  count++;
                  sc.setAttribute("count",count);
              }
          }
          

          這時我們發現對于在線人數的統計,不是網頁訪問的次數,也不是瀏覽器打開的個數,對需求的理解的錯誤理解。所以正確的做法是統計其IP的數量,這樣的話,不管你在一臺電腦上開啟多少客戶端,都會只有一個。

          正確的統計方法

          統計其IP的數量,將IP的數量作為當前的在線人數,那么如何統計IP的數量呢?這樣將會導出以下問題:

          • 如何獲取用戶的IP?
          • IP將如何存儲?
          • 如何判斷IP之前已經存在?

          現在來解決這些問題:

          • 只能從請求中獲取
          • 通過2、3問題,我們想到了集合(List),因為集合不僅可以存儲任何字符串,還可以通過遍歷來判斷之前是否有重復的IP出現。

          到了這里又冒出來一個問題集合(List)放到哪個域里呢?

          ServletContext域

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          import java.util.ArrayList;
          import java.util.List;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
          
              @Override
              //Application被初始化的時候創建
              public void contextInitialized(ServletContextEvent sce) {
                  //創建一個鏈表來存儲IP
                  List<String> ips = new ArrayList<>();
                  sc = sce.getServletContext();
                  //將創建好的鏈表對象,放到Application域中
                  sc.setAttribute("ips",ips);
              }
          }
          

          由于IP只能在Request域中獲取,所以遍歷判斷在Request域中進行。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
          
              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;
          
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr = (HttpServletRequest) sre.getServletRequest();
                  clientIp = sr.getRemoteAddr();
                  session = sr.getSession();
                  session.setAttribute("clientIp",clientIp);
          
                  //測試
                  // System.out.println("clientIp = "+ clientIp);
                  //獲取Application域中的List
                  sc = sre.getServletContext();
                  ips = (List<String>) sc.getAttribute("ips");
                  //遍歷ips
                  for (String ip :
                          ips) {
                      if (clientIp.equals(ip))
                          return;
                  }
                  ips.add(clientIp);
                  sc.setAttribute("ips",ips);
              }
          }

          因為要統計在線人數,所以要設置退出按鈕,點擊退出按鈕之后,因為要從List域中移除,所以使用Session域監聽器來判斷session回話的關閉

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;
              private Object clientIp;
          
              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();
                  ips = (List<String>) sc.getAttribute("ips");
                  session = se.getSession();
                  clientIp = session.getAttribute("clientIp");
                  //刪除ip,如何獲取IP,但是不可以從session獲取到IP
                  //因為Session獲取不到Request
                  //一個Session包含多個Request
                  //一個Request只對應一個Session 所以獲取不到,這時只能先從Request域中獲取到的ips,放置到Session域
                  //然后從Session 域中讀取
                  ips.remove(clientIp);
                  // session一失效就馬上將此IP從鏈表中移除是錯誤的
                  //應該看此IP是否有另外的回話存在,如果有的話不能刪除
              }
          }
          

          此處代碼是頁面點擊關閉后,激活的退出方法

          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;
          import java.io.IOException;
          
          @WebServlet(name = "LogoutServlet",urlPatterns = "/logoutServlet")
          public class LogoutServlet extends HttpServlet {
          
              private HttpSession session;
          
              protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  //從域中獲取一個session,設置為false 如果域中存在一個session,則直接獲取,如果不存在,則返回一個空的session
                  session = request.getSession(false);
                  if (session != null){
                      //使session失效
                      session.invalidate();
                      //失效后,需要進行的操作,List鏈表中需要減去,用到了Session域監聽器
                  }
              }
          
              protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  doPost(request,response);
              }
          }
          

          在jsp頁面進行讀取的時候,因為ips是以List鏈表的形式存在的,所以要想判斷當前在線人數,所以必須要判斷鏈表的長度,所以是applicationScope.ips.size()

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.ips.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a></h3>
            </center>
            </body>
          </html>
          

          好了?,這時候,程序寫完了,如何判斷呢?

          此時,我們的程序是部署在本地的Tomcat上的,對于本臺電腦,只有一個IP,如何實現多個IP呢?其實啊我們的電腦可以有三個IP,在訪問服務器的時候,服務器的IP多寫幾個,相當于本機的IP多出來幾個。是哪三個IP呢?

          1、默認clientIp : 0:0:0:0:0:0:0:1

          2、127.0.0.1

          這時大家可能會問127.0.0.1和localhost有什么區別呢,其實在這里要區分三個概念:

          localhost、127.0.0.1 和 本機IP之間的區別:

          • localhost等于127.0.0.1,不過localhost是域名,127.0.0.1是IP地址。
          • localhost和127.0.0.1不需要聯網,都是本機訪問。
          • 本機IP需要聯網,本機IP是本機或外部訪問, 本機 IP 就是本機對外放開訪問的IP地址,這個網址就 是與物理網卡綁定的IP地址。

          3、IPv4地址:192.168.1.110

          這樣就很完美的實現了本地三個IP的測試。

          寫到這里,似乎已經可以簡單的測試當前在線人數,也許仔細的人會發現在Session域被銷毀的方法中的注釋中發現一些貓膩。大家可以仔細想想,如果客戶端用不同的瀏覽器,相同的IP去訪問呢?點擊退出后,會不會出現錯誤情況呢?答案是會的。演示結果如下圖

          最完美的代碼

          所以在點擊退出登錄的按鈕之后,不可以直接將IP移除,要判斷有沒有另外的回話存在,如果有另外的回話存在,此IP是不可以刪掉的,問題由此變的復雜了,因為還要統計此IP所發出的會話有多少。

          整體思路:

          在全局域中,將不是直接將iP存放在List的鏈表中,而是以一個Map的形式存在,Map的鍵為String類型,Key為List類型,List中存放的是當前IP所激發的會話對象,這樣就可以統計,一個IP觸發的sessions有多少個。

          通過調用Map的get方法,將當前IP最為參數,將可以獲取到他所激發的會話集合。但是,此集合可能為空,因為有可能當前IP一次也沒有訪問此頁面,所以在List為空的時候好要創建一個ArrayList來存放sessions,然后將變化后的List重新寫回到Map,再將變化后的Map寫回到全局域中 。這樣創建過程基本完成。

          然后考慮銷毀過程,IP還需方法放到Session域中,當session被銷毀的時候,應該把當前Session從List 中刪除,但是Map中此sessions對應的IP可是不能直接刪,要判斷List中的sessions的個數(Entry對象),個數為1的時候才可以刪除,不然就不可以刪除。

          所以,要將當前IP通過Request域存放到當前Session域中,

          然后,要考慮的問題是,每次刷新頁面后sessions的個數會增加,這是錯誤的,原因是什么?

          答案是,因為在存放sessions的時候,創建數組直接進行的添加,這樣的話,每次一刷新頁面,就會導致sessions的添加,所以在此之前應該判斷,sessions中是否有此session,有的話直接跳出。

          這樣添加就沒問題了

          Servlet域中添加Map

          在Map中,需要使用鍵值對的方式,Key為IP,Value為List,那么List中存放什么呢?存放的是此IP發出的所有回話的HttpSession的對象,所以List的泛型是HttpSession。

          請求,在請求中,因為將當前Session 對象存放到List中, List在Map中,Map在全局域中,所以首先得從全局域獲取到Map,然后,從Map中獲取由當前IP所發出的所有Session的組成的List,判斷當前的List是否為NULL,若為NULL,則創建List,否則,將當前SessioncurrentSession放入List中。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
          
              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;
          
          
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr = (HttpServletRequest) sre.getServletRequest();
                  clientIp = sr.getRemoteAddr();
                  currentSession  = sr.getSession();
                  //將當前Session 對象存放到List中, List在Map中,Map在全局域中,
                  sc = sre.getServletContext();
                  map = (Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取由當前IP所發出的所有Session的組成的List
                  sessions = map.get(clientIp);
                  //判斷當前的List是否為NULL,若為NULL,則創建List,否則,將當前Session放入List
                  if (sessions == null){
                      sessions = new ArrayList<>();
                  }
          //        遍歷List的session 對象,若有則不添加,若沒有則添加
                  for (HttpSession session :
                          sessions) {
                      if (session == currentSession)
                          return;
                  }
                  sessions.add(currentSession);
          
          
                  //將變化過的List重新寫回到Map
                  map.put(clientIp,sessions);
                  //再將變化的Map寫回到全局域中
                  sc.setAttribute("map",map);
          
                  //將當前IP放入到當前Session
                  currentSession.setAttribute("clientIp",clientIp);
              }
          
          }
          

          ServletContext

          這里將不使用ips了,所以將其刪除

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.HashMap;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創建
              public void contextInitialized(ServletContextEvent sce) {
                  //創建一個Map,key為IP,value為該IP上所發出的會話的對象
                  Map<String,List<HttpSession>> map = new HashMap<>();
                  sc = sce.getServletContext();
                  //將map放到全局域中
                  sc.setAttribute("map",map);
              }
          }
          

          Session監聽器

          接下來剖析Session的刪除工作,獲取當前Session對象,這里有之前傳遞過來的IP,在進行刪除操作的時候,要注意此處,刪除的是List中的sessions,刪除之后,還要判斷其IP的是否要刪除,如果List中沒有該元素,則說明當前IP所發出的會話全部關閉,就可以從map中將當前IP對應的Entry對象刪除,否則,當前IP所發出的會話任存在,那么使用put方法將變化過的List寫回到map。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private String clientIp;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;
          
              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();
          
                  currentSession = se.getSession();
                  clientIp = (String) currentSession.getAttribute("clientIp");
                  map = (Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取List
                  sessions = map.get(clientIp);
                  //從List中刪除當前Session對象
                  sessions.remove(currentSession);
                  //如果List中沒有該元素,則說明當前IP所發出的會話全部關閉,就可以從map中
                  //將當前IP對應的Entry對象刪除
                  //若List中仍有元素,當前IP所發出的會話任存在,那么將變化過的List寫回到map
                   if (sessions.size() == 0){
                       map.remove(clientIp);
                   }else {
                       map.put(clientIp,sessions);
                   }
                   sc.setAttribute("map",map);
              }
          }
          

          因為處理的退出的頁面/logoutServlet不需要做任何不同的處理,所以這里將不再重復。

          因為在jsp用到了JSP標準庫,所以到導兩個包。

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.map.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a><br></h3>
              <h2>
                <c:forEach items="${map}" var="entry">
                  ${entry.key }=${entry.value.size()}<br>
                </c:forEach>
              </h2>
            </center>
            </body>
          </html>
          

          最后 測試成功,這就是一個完美的統計當前用戶的在線人數。

          來源:https://mp.weixin.qq.com/s/gM-lyh_9FeFKRRAJlPY83g


          主站蜘蛛池模板: 91精品一区国产高清在线| 国产凹凸在线一区二区| 精品在线一区二区三区| 日韩精品一区二区午夜成人版| 国产福利一区二区在线视频 | 一区二区三区在线| 国内自拍视频一区二区三区 | 97人妻无码一区二区精品免费| 亚洲福利视频一区| 亚洲色精品aⅴ一区区三区| 久久精品无码一区二区三区日韩| 亚洲日韩AV无码一区二区三区人 | 久久99国产精一区二区三区| 国产无码一区二区在线| 久99精品视频在线观看婷亚洲片国产一区一级在线| 一区二区三区福利视频免费观看| 精品国产一区二区三区AV性色| 色欲AV无码一区二区三区| 精品乱码一区二区三区在线| 亚洲AV无码一区二区三区网址| 亚洲电影国产一区| 精品欧洲av无码一区二区14| 国产精品自拍一区| 69福利视频一区二区| 亚洲乱码av中文一区二区| 国产精品香蕉一区二区三区| 一区二区三区无码被窝影院| 人妻av综合天堂一区| 国产成人精品一区二三区| 日韩一区二区三区精品| 精品无码综合一区| 污污内射在线观看一区二区少妇 | 国产精品熟女一区二区| 无码人妻久久久一区二区三区| 亚洲一区精品中文字幕| 国产成人亚洲综合一区| 国产精品亚洲专区一区| 精品一区二区三区高清免费观看 | 麻豆AV无码精品一区二区 | 91久久精品无码一区二区毛片| 欧美成人aaa片一区国产精品|