JSP在請求監聽器中調用application的自定義屬性時,消除空指針報錯(java.lang.NullPointerException)的方法

【問題背景

  JSP開發中,利用Listener監聽器對象,可以記錄某網站訪客的到訪時間、訪客IP地址等信息。

  注意這裏的一個訪客對應一個session對象,而不是具體的某人。

  本例中,統計了兩類信息:一是訪客相關信息;二是瀏覽歷史信息。

   1、前者字段包括:到訪ID(主鍵,整型,自動遞增)、到訪時間、離開時間(session過期時刻)、IP地址、來源網址等信息,存入數據庫visitorDB的visitor表中。通過一個【歷史訪客】按鈕,將表中內容以“歷史訪客表”的形式顯示到一個JSP頁面。

   2、後者字段包括:瀏覽記錄ID(主鍵,整型,自動遞增)、到訪ID(即visitor表的主鍵),訪問頁面時刻,和瀏覽頁面URL。所有記錄存入history表中,通過一個【瀏覽軌跡】按鈕,將表中內容以“訪問記錄表”的形式顯示到另一個JSP頁面。

  由於出現了到訪ID這個外鍵,在讀寫history表時,需要調用當前session對應的訪客v的主鍵值。實現方法大致兩種:

   (1)在HashMap<String, Visitor>中,調用當前sessionID的鍵值對,用map.get(sessionID)獲取該Visitor對象。這裏的 key=sessionID=arg0.getSession().getId();

   (2)將當前訪客Visitor visitorCurrent單獨保存到session的自定義屬性"USER"中,再到requestInitialized()方法中調用該屬性值:

    保存:

     HttpSession session = arg0.getSession();

     session.setAttribute("USER",visitorCurrent);

    提取:

     Visitor vCurrent =(Visitor) request.getSession().getAttribute("USER");

  本例使用方法(2)。

【問題描述】

  按照方法(2)保存當前訪客對象visitorCurrent,本應保存到session中,結果根據自動提示錯誤保存到了application中,寫成了:Visitor vCurrent = (Visitor) request.getServletContext().getAttribute("USER");

  於是將錯就錯,將保存語句也一併改成:

   ServletContext application = arg0.getSession().getServletContext();

   application.setAttribute("USER",visitorCurrent);

  啓動服務器驗證代碼,結果在調用訪客ID時報空指針錯誤:java.lang.NullPointerException,問題所在代碼爲:

   h.setVisitId( vCurrent.getId() );

【原因分析】

  沒弄清各監聽方法的執行順序。啓動服務器時,Listener先調用“監聽服務器啓動”的方法contextInitialized(),然後調用“監聽頁面請求啓動”的方法requestInitialized(),處理請求過程中,會根據需要創建session對象,由此觸發“監聽session創建”的方法sessionCreated()。

  這樣,報空指針估計和異步執行有關,調用vCurrent的id值的時候,vCurrent還沒從定義在session中的屬性“USER”中,獲取到Visitor對象,因此出現空指針。要消除該異常,

   ①要麼調用前判定有沒有提前保存,有才賦值,沒有則追加;

   ②要麼在服務器啓動時就預先保存一個空的Visitor對象visitorEmpty;

  優先選用①,因爲②中的空Visitor意味着此時的訪客ID=0,與實際情況不符;而①可以指定當前sessionID對應的Visitor,因此更合理。

【解決方案】

  在h.setVisitId( vCurrent.getId() );之前加一句if判定語句,如果“USER”屬性值爲空,則追加sessionID對應的Visitor對象:

ServletContext app = arg0.getServletContext();
if( app.getAttribute( "USER" ) == null ) {
  @SuppressWarnings( "unchecked" )
  HashMap<String, Visitor> map = ( HashMap<String, Visitor> ) app.getAttribute( "ONLINE" );
  Visitor vCurrent = map.get( request.getSession().getId() );
  arg0.getServletContext().setAttribute("USER", vCurrent);
}
Visitor vNeeded = (Visitor) app.getAttribute("USER");
h.setVisitId( vNeeded.getId() );

  驗證代碼,空指針錯誤消除,顯示的訪客ID也沒有出現0值錯誤,問題解決。

【注意事項】

  本例將錯就錯,把外鍵所在的對象vCurrent保存到application屬性中,雖然通過驗證,但邏輯上仍有重大問題。因爲application的屬性只有“USER”一個,當多個用戶同時調用該屬性值時,只能得到一個最晚請求調用的當前訪客,之前的訪客對象將被後來者覆蓋。因此保存到當前session的屬性中,才能避免相互覆蓋,不同的訪客才能看到各自對應的瀏覽記錄。

  本例旨在說明至少以下兩個問題:

   1、requestInitialized()方法可能先於sessionCreated()執行,若跨方法調用值報告空指針異常(java.lang.NullPointerException);

   2、明確出現空指針異常的原因,並知道如何修改代碼消除該異常;但最終還要考慮實際處理邏輯。

發佈了38 篇原創文章 · 獲贊 23 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章