ServletContext可以初始化String類型的參數。但是,如果你希望應用初始化參數是一個數據庫DataSource呢?
上下文參數只能是String。畢竟,你不能把一個Dog對象塞到XML部署描述文件中(事實上,可以用XML表示一個串行化對象,但是在當前的Servlet規范中還沒有相關的支持……沒准將來會提供)。
如果你真的想讓Web應用的所有部分都能訪問一個共享的數據連接,該怎么辦?
當然可以把這個DataSource查找名放在一個上下文初始化參數里,這也是當前上下文參數最常見的一種用法。
不過,之后誰將這個String參數轉換成由Web應用各部分共享的一個具體DataSource引用呢?
不能把這個代碼放在servlet中,因為你選擇誰作為第一個servlet來查找DataSource並把它存儲在一個屬性中呢?你真的想保證總是讓某個特定的servlet最先運行嗎?好好考慮一下。
解決辦法:
可以建立一個單獨的類,而不是一個servlet或JSP,它能監聽ServletContext一生中的兩個關鍵事件:初始化(創建)和撤銷。這個類實現了javax.servlet.ServletContextListener。
ServletContextListener類:
package com.moonlit; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent event) { // 初始化數據庫連接的代碼 // and store it as a context attribute } @Override public void contextInitialized(ServletContextEvent event) { // 關閉數據庫連接的代碼 } }
教程:一個簡單的ServletContextListener
在這個例子中,我們要把String初始化參數轉換成一個真正的對象——一個Dog。監聽者的任務是得到有關狗的品種的上下文初始化參數(Beagle、Poodle、等等),然后使用這個String來構造一個Dog對象。監聽者再把這個Dog對象存到一個ServletContext屬性中,以便servlet獲取。
關鍵是,servlet現在能訪問一個共享的數據對象(在這里,就是一個共享的Dog),而且不用讀上下文對象。這個共享的對象是一個Dog還是一個數據庫連接並沒有關系。重點是要使用初始化參數來創建一個對象,讓應用的所有部分都能共享這個對象。
這個例子將包括如下過程:
□ 監聽者對象向ServletContextEvent對象請求應用ServletContext對象的一個引用。
□ 監聽者使用這個ServletContext引用得到"breed"上下文初始化參數,這是一個String,表示狗的品種。
□ 監聽者使用這個狗的品種String構造一個Dog對象。
□ 監聽者使用ServletContext引用在ServletContext中設置Dog屬性。
□ 這個Web應用的測試servlet從ServletContext得到Dog對象,並調用這個Dog的getBreed()方法。
建立和使用一個上下文監聽者
要建立和使用一個上下文監聽者,我們需要在web.xml中部署描述文件,在web.xml中添加如下內容:
<listener> <listener-class> com.moonlit.MyServletContextListener </listener-class> </listener>
除了設置ServletContextListener,我們還要設置ServletContext的初始化參數,以及測試的servlet的相關的參數。
完整的web.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>DogListenerExample</display-name> <servlet> <servlet-name>ListenerTester</servlet-name> <servlet-class>com.moonlit.ListenerTester</servlet-class> </servlet> <servlet-mapping> <servlet-name>ListenerTester</servlet-name> <url-pattern>/ListenerTester.do</url-pattern> </servlet-mapping> <context-param> <param-name>breed</param-name> <param-value>Great Dane</param-value> </context-param> <listener> <listener-class> com.moonlit.MyServletContextListener </listener-class> </listener> </web-app>
MyServletContextListener是監聽者類。這個類實現了ServletContextListener,它得到上下文初始化參數,創建Dog,並把Dog作為一個上下文屬性。
MyServlerContextListener.java:
package com.moonlit; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) { ServletContext sc = event.getServletContext(); String dogBreed = sc.getInitParameter("breed"); Dog d = new Dog(dogBreed); sc.setAttribute("dog", d); } @Override public void contextDestroyed(ServletContextEvent event) { // do nothing here } }
我們需要建立一個屬性類Dog。DOg只是一個普通的Java類,它的任務是作為一個屬性值,由ServletContextListener實例化,並設置在ServletContext中,一遍servlet獲取。
Dog.java:
package com.moonlit; public class Dog { private String breed; public Dog(String breed) { this.breed = breed; } public String getBreed() { return breed; } }
ListenerTest類是一個Servlet類,它的任務是驗證監聽者的工作。為此這個Servlet會從上下文得到Dog屬性,調用Dog的getBreed(),把結果打印到響應(使我們能在瀏覽器中看到)。
ListenerTester.java:
package com.moonlit; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class ListenerTester extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); PrintWriter out = resp.getWriter(); out.println("test context attributes set by listener<br>"); out.println("<br>"); Dog dog = (Dog) getServletContext().getAttribute("dog"); out.println("Dog's breed is : " + dog == null ? "no " : dog.getBreed()); } }
效果如下: