以下內容是翻譯自http://www.journaldev.com/1945/servletcontextlistener-servlet-listener-example:
說明:web.xml的加載順序是:【Context-Param】->【Listener】->【Filter】->【Servlet】,而同個類型之間的實際程序調用的時候的順序是根據對應的Mapping的順序進行調用。
為什么使用Servlet Listener?
我們知道使用ServletContext,我們可以創建一個具有所有其他servlet可以訪問的應用范圍的屬性,但是我們可以在部署描述符(web.xml)中將ServletContext init參數初始化為String。如果我們的應用程序是面向數據庫的,並且我們要在數據庫連接的ServletContext中設置一個屬性,該怎么辦?如果應用程序具有單個入口點(用戶登錄),那么可以在第一個Servlet請求中執行,但是如果我們有多個入口點,那么在任何地方都會執行代碼冗余。另外,如果數據庫關閉或配置不正確,我們將不會知道,直到第一個客戶端請求到達服務器。為了處理這些情況,Servlet API提供了Listener接口,我們可以實現和配置監聽事件並執行某些操作。
事件(Event)是發生的事情,在Web應用程序世界中,事件可以是應用程序的初始化,銷毀應用程序,從客戶端請求,創建/銷毀會話,會話中的屬性修改等。
Servlet API提供了不同類型的偵聽器接口,我們可以在web.xml中實現和配置,以便在特定事件發生時處理某些事件。例如,在上述情況下,我們可以為應用程序啟動事件創建一個偵聽器來讀取上下文初始化參數並創建數據庫連接,並將其設置為上下文屬性以供其他資源使用。
Servlet Listener接口和事件(Event)對象
Servlet API為不同類型的事件提供了不同類型的偵聽器。偵聽器接口聲明方法來處理一組類似的事件,例如我們有ServletContext Listener監聽上下文的啟動和關閉事件。偵聽器界面中的每個方法都將事件對象作為輸入。事件對象作為一個包裝器,為偵聽器提供特定的對象。
Servlet API提供以下事件對象:
- javax.servlet.AsyncEvent - 在ServletRequest(通過調用ServletRequest#startAsync或ServletRequest#startAsync(ServletRequest,ServletResponse))啟動的異步操作已完成,超時或產生錯誤時觸發的事件。
- javax.servlet.http.HttpSessionBindingEvent - 將此類型的事件發送到實現HttpSessionBindingListener的對象,當該對象從會話綁定或解除綁定時,或者發送到在web.xml中配置的HttpSessionAttributeListener,當綁定任何屬性時,在會話中取消綁定或替換。會話通過對HttpSession.setAttribute的調用來綁定對象,並通過調用HttpSession.removeAttribute解除對象的綁定。當對象從會話中刪除時,我們可以使用此事件進行清理活動。
- javax.servlet.http.HttpSessionEvent - 這是表示Web應用程序中會話更改的事件通知的類。
- javax.servlet.ServletContextAttributeEvent - 關於對Web應用程序的ServletContext的屬性進行更改的通知的事件類。
- javax.servlet.ServletContextEvent - 這是關於Web應用程序的servlet上下文更改的通知的事件類。
- javax.servlet.ServletRequestEvent - 此類事件表示ServletRequest的生命周期事件。事件的源代碼是這個Web應用程序的ServletContext。
- javax.servlet.ServletRequestAttributeEvent - 這是事件類,用於對應用程序中servlet請求的屬性進行更改的通知。
Servlet API提供了以下監聽器接口:
- javax.servlet.AsyncListener - 如果在添加了偵聽器的ServletRequest上啟動的異步操作已完成,超時或導致錯誤,將會通知偵聽器。
- javax.servlet.ServletContextListener - 用於接收關於ServletContext生命周期更改的通知事件的接口。
- javax.servlet.ServletContextAttributeListener - 接收關於ServletContext屬性更改的通知事件的接口。
- javax.servlet.ServletRequestListener - 用於接收關於進入和超出Web應用程序范圍的請求的通知事件的接口。
- javax.servlet.ServletRequestAttributeListener - 接收關於ServletRequest屬性更改的通知事件的接口。
- javax.servlet.http.HttpSessionListener - 接收關於HttpSession生命周期更改的通知事件的接口。
- javax.servlet.http.HttpSessionBindingListener - 使對象從會話綁定到綁定或從其綁定時被通知。
- javax.servlet.http.HttpSessionAttributeListener - 用於接收關於HttpSession屬性更改的通知事件的接口。
- javax.servlet.http.HttpSessionActivationListener - 綁定到會話的對象可能會偵聽容器事件,通知他們會話將被鈍化,該會話將被激活。需要在VM或持久化會話之間遷移會話的容器來通知綁定到實現HttpSessionActivationListener的會話的所有屬性。
Servlet Listener配置
我們可以使用@WebListener注解來聲明一個類作為Listener,但是該類應該實現一個或多個Listener接口。
我們可以在web.xml中定義listener:
<listener> <listener-class> com.journaldev.listener.AppContextListener </listener-class> </listener>
Servlet Listener示例
讓我們創建一個簡單的Web應用程序來查看Servlet偵聽器的操作。我們將在Eclipse ServletListenerExample中創建動態Web項目,這些項目結構將如下圖所示。

web.xml:在部署描述符中,我將定義一些上下文初始化參數和監聽器配置。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>ServletListenerExample</display-name> <context-param> <param-name>DBUSER</param-name> <param-value>pankaj</param-value> </context-param> <context-param> <param-name>DBPWD</param-name> <param-value>password</param-value> </context-param> <context-param> <param-name>DBURL</param-name> <param-value>jdbc:mysql://localhost/mysql_db</param-value> </context-param> <listener> <listener-class>com.journaldev.listener.AppContextListener</listener-class> </listener> <listener> <listener-class>com.journaldev.listener.AppContextAttributeListener</listener-class> </listener> <listener> <listener-class>com.journaldev.listener.MySessionListener</listener-class> </listener> <listener> <listener-class>com.journaldev.listener.MyServletRequestListener</listener-class> </listener> </web-app>
DBConnectionManager:這是數據庫連接的類,為了簡單起見,我沒有為實際的數據庫連接提供代碼。我們將這個對象設置為servlet上下文的屬性。
package com.journaldev.db; import java.sql.Connection; public class DBConnectionManager { private String dbURL; private String user; private String password; private Connection con; public DBConnectionManager(String url, String u, String p){ this.dbURL=url; this.user=u; this.password=p; //create db connection now } public Connection getConnection(){ return this.con; } public void closeConnection(){ //close DB connection here } }
MyServlet:一個簡單的servlet類,我將使用會話,屬性等。
package com.journaldev.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletContext; 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; @WebServlet("/MyServlet") public class MyServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext ctx = request.getServletContext(); ctx.setAttribute("User", "Pankaj"); String user = (String) ctx.getAttribute("User"); ctx.removeAttribute("User"); HttpSession session = request.getSession(); session.invalidate(); PrintWriter out = response.getWriter(); out.write("Hi "+user); } }
現在我們將實現監聽器類,我為常用的監聽器提供了示例偵聽器類 - ServletContextListener,ServletContextAttributeListener,ServletRequestListener和HttpSessionListener。
ServletContextListener
我們將讀取servlet context init參數來創建DBConnectionManager對象,並將其設置為ServletContext對象的屬性。
package com.journaldev.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import com.journaldev.db.DBConnectionManager; @WebListener public class AppContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext ctx = servletContextEvent.getServletContext(); String url = ctx.getInitParameter("DBURL"); String u = ctx.getInitParameter("DBUSER"); String p = ctx.getInitParameter("DBPWD"); //create database connection from init parameters and set it to context DBConnectionManager dbManager = new DBConnectionManager(url, u, p); ctx.setAttribute("DBManager", dbManager); System.out.println("Database connection initialized for Application."); } public void contextDestroyed(ServletContextEvent servletContextEvent) { ServletContext ctx = servletContextEvent.getServletContext(); DBConnectionManager dbManager = (DBConnectionManager) ctx.getAttribute("DBManager"); dbManager.closeConnection(); System.out.println("Database connection closed for Application."); } }
ServletContextAttributeListener
在servlet上下文中添加,刪除或替換屬性時,記錄事件的簡單實現。
package com.journaldev.listener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; import javax.servlet.annotation.WebListener; @WebListener public class AppContextAttributeListener implements ServletContextAttributeListener { public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("ServletContext attribute added::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}"); } public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("ServletContext attribute replaced::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}"); } public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("ServletContext attribute removed::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}"); } }
HttpSessionListener
創建或銷毀會話時記錄事件的簡單實現。
package com.journaldev.listener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; @WebListener public class MySessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent sessionEvent) { System.out.println("Session Created:: ID="+sessionEvent.getSession().getId()); } public void sessionDestroyed(HttpSessionEvent sessionEvent) { System.out.println("Session Destroyed:: ID="+sessionEvent.getSession().getId()); } }
ServletRequestListener
ServletRequestListener接口的簡單實現,用於在請求初始化和銷毀時記錄ServletRequest IP地址。
package com.journaldev.listener; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; @WebListener public class MyServletRequestListener implements ServletRequestListener { public void requestDestroyed(ServletRequestEvent servletRequestEvent) { ServletRequest servletRequest = servletRequestEvent.getServletRequest(); System.out.println("ServletRequest destroyed. Remote IP="+servletRequest.getRemoteAddr()); } public void requestInitialized(ServletRequestEvent servletRequestEvent) { ServletRequest servletRequest = servletRequestEvent.getServletRequest(); System.out.println("ServletRequest initialized. Remote IP="+servletRequest.getRemoteAddr()); } }
現在,當我們將使用URL部署我們的應用程序並在瀏覽器中訪問MyServlet時http://localhost:8080/ServletListenerExample/MyServlet,我們將在服務器日志文件中看到以下日志。
ServletContext attribute added::{DBManager,com.journaldev.db.DBConnectionManager@4def3d1b} Database connection initialized for Application. ServletContext attribute added::{org.apache.jasper.compiler.TldLocationsCache,org.apache.jasper.compiler.TldLocationsCache@1594df96} ServletRequest initialized. Remote IP=0:0:0:0:0:0:0:1%0 ServletContext attribute added::{User,Pankaj} ServletContext attribute removed::{User,Pankaj} Session Created:: ID=8805E7AE4CCCF98AFD60142A6B300CD6 Session Destroyed:: ID=8805E7AE4CCCF98AFD60142A6B300CD6 ServletRequest destroyed. Remote IP=0:0:0:0:0:0:0:1%0 ServletRequest initialized. Remote IP=0:0:0:0:0:0:0:1%0 ServletContext attribute added::{User,Pankaj} ServletContext attribute removed::{User,Pankaj} Session Created:: ID=88A7A1388AB96F611840886012A4475F Session Destroyed:: ID=88A7A1388AB96F611840886012A4475F ServletRequest destroyed. Remote IP=0:0:0:0:0:0:0:1%0 Database connection closed for Application.
注意日志的順序,它按照執行的順序。當您關閉應用程序或關閉容器時,將顯示最后一個日志。
這是Servlet中的所有的監聽器。
測試工程:https://github.com/easonjim/5_java_example/tree/master/servletbasics/test20
