ServletContextListener Servlet侦听器示例
ServletContextListener是我们拥有的众多Servlet侦听器之一。
这是Java Web Application系列的第五篇文章,您可能还想查阅前面的四篇文章。
- Java Web应用程序
- Java中的Servlet
- Servlet会话管理
- Servlet过滤器
Servlet侦听器
在本教程中,我们将研究Servlet侦听器,Servlet侦听器的优点,可以使用侦听器执行的一些常见任务,Servlet API侦听器接口和Event对象。
最后,我们将创建一个简单的Web项目,以显示ServletContext,Session和ServletRequest的常用侦听器实现的示例。
- 为什么我们有Servlet Listener?
- Servlet侦听器接口和事件对象
- Servlet侦听器配置
- Servlet侦听器示例
- ServletContextListener
- ServletContextAttributeListener
- HttpSessionListener
- ServletRequestListener
- 为什么我们有Servlet Listener?
我们知道使用ServletContext可以创建具有所有其他Servlet都可以访问的应用程序范围的属性,但是我们只能在部署描述符(web.xml)中将ServletContext初始化参数初始化为String。
如果我们的应用程序是面向数据库的,并且我们想在ServletContext中为数据库连接设置一个属性,该怎么办?如果您的应用程序只有一个入口点(用户登录),那么您可以在第一个servlet请求中进行操作,但是如果我们有多个入口点,那么在任何地方都进行操作将导致大量代码冗余。
另外,如果数据库已关闭或者配置不正确,则只有在第一个客户端请求到达服务器之前,我们才知道。
为了处理这些情况,Servlet API提供了侦听器接口,我们可以将其实现和配置为侦听事件并执行某些操作。
事件是某事的发生,在Web应用程序世界中,事件可以是应用程序初始化,破坏应用程序,来自客户端的请求,创建/销毁会话,在会话中进行属性修改等。
Servlet API提供了不同类型的侦听器接口,我们可以在web.xml中实现和配置这些侦听器接口,以在发生特定事件时处理某些内容。
例如,在上述情况下,我们可以为应用程序启动事件创建一个侦听器,以读取上下文初始化参数并创建数据库连接,并将其设置为上下文属性以供其他资源使用。
- Servlet侦听器接口和事件对象
Servlet API为不同类型的事件提供了不同种类的侦听器。
侦听器接口声明用于与一组类似事件一起工作的方法,例如,我们有ServletContext侦听器来侦听上下文的启动和关闭事件。
侦听器接口中的每个方法都将Event对象作为输入。
事件对象充当包装器,以向侦听器提供特定的对象。
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侦听器配置
我们可以使用@WebListener批注将一个类声明为Listener,但是该类应实现一个或者多个Listener接口。
我们可以在web.xml中将侦听器定义为:
<listener> <listener-class> com.theitroad.listener.AppContextListener </listener-class> </listener>
- Servlet侦听器示例
让我们创建一个简单的Web应用程序,以查看servlet侦听器的运行情况。
我们将在Eclipse ServletListenerExample中创建动态Web项目,这些项目结构如下图所示。
web.xml:在部署描述符中,我将定义一些上下文初始化参数和侦听器配置。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://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>hyman</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.theitroad.listener.AppContextListener</listener-class> </listener> <listener> <listener-class>com.theitroad.listener.AppContextAttributeListener</listener-class> </listener> <listener> <listener-class>com.theitroad.listener.MySessionListener</listener-class> </listener> <listener> <listener-class>com.theitroad.listener.MyServletRequestListener</listener-class> </listener> </web-app>
DBConnectionManager:这是数据库连接的类,为简单起见,我不提供用于实际数据库连接的代码。
我们将这个对象设置为servlet上下文的属性。
package com.theitroad.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.theitroad.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", "hyman"); 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上下文初始化参数来创建DBConnectionManager对象,并将其设置为ServletContext对象的属性。
package com.theitroad.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import com.theitroad.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.theitroad.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.theitroad.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.theitroad.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()); } }
现在,当我们部署应用程序并使用URLhttps://localhost:8080/ServletListenerExample/MyServlet
在浏览器中访问MyServlet时,我们将在服务器日志文件中看到以下日志。
ServletContext attribute added::{DBManager,com.theitroad.db.DBConnectionManager@4def3d1b} Database connection initialized for Application. ServletContext attribute added::{org.apache.jasper.compiler.comLocationsCache,org.apache.jasper.compiler.comLocationsCache@1594df96} ServletRequest initialized. Remote IP=0:0:0:0:0:0:0:1%0 ServletContext attribute added::{User,hyman} ServletContext attribute removed::{User,hyman} 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,hyman} ServletContext attribute removed::{User,hyman} 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.
请注意日志的顺序,并按执行顺序排列。
当您关闭应用程序或者关闭容器时,将显示最后一个日志。