與 ServletContext
相關的傾聽器有 ServletContextListener
與 ServletContextAttributeListener
。
如果想要知道何時 Web 應用程式已經初始化或即將結束銷毀,你可以實作 ServletContextListener
:
package javax.servlet;
import java.util.EventListener;
public interface ServletContextListener extends EventListener {
public default void contextInitialized(ServletContextEvent sce) {}
public default void contextDestroyed(ServletContextEvent sce) {}
}
在 Web 應用程式初始化後或即將結束銷毀前,會呼叫 ServletContextListener
實作類別相對應的 contextInitialized()
或 contextDestroyed()
。你可以在 contextInitialized()
中實作應用程式資源的準備動作,在 contextDestroyed()
實作釋放應用程式資源的動作。在 Servlet 4.0 中,這兩個方法都是 default
,對感興趣的方法實作即可。
例如說,你可以實作 ServletContextListener
,在應用程式初始過程中,準備好資料庫連線物件、讀取應用程式設定等動作,像是放置使用頭像的目錄資訊,不宜將目錄資訊寫死在程式碼,以免日後目錄變動名稱或位置時,所有相關的 Servlet 都需要進行原始碼的修改,這時你可以這麼作:
package cc.openhome;
...
public class AvatarInitializer implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
String avatars = context.getInitParameter("AVATAR");
context.setAttribute("avatars", avatars);
...
}
...
}
當 Web 容器呼叫 contextInitialized()
或 contextDestroyed()
時,會傳入 ServletContextEvent
,其封裝了 ServletContext
,你可以透過 ServletContextEvent
的 getServletContext()
方法取得 ServletContext
,之後就可以進行 ServletContext
初始參數的讀取。
你可以在web.xml中如下定義:
...
<context-param>
<param-name>AVATAR</param-name>
<param-value>/avatars</param-value>
</context-param>
<listener>
<listener-class>cc.openhome.AvatarInitializer</listener-class>
</listener>
...
在 web.xml 中,使用 <context-param>
標籤來定義初始參數,並使用了 <listener>
與 <listener-class>
標籤來定義實作了 ServletContextListener
介面的類別名稱。ServletContextListener
也可以直接使用 @WebListener
標註,如此容器就會在啟動時載入並執行對應的方法,但因 @WebListener
沒有設定初始參數的屬性,所以僅適用於無需設置初始參數的情況。
有些應用程式的設定,必須在 Web 應用程式初始時進行,例如〈HttpSession 原理〉中談過,可以取得 ServletContext
而後進行 SessionCookieConfig
的設定,這就必須在應用程式初始時進行,例如,透過 SessionCookieConfig
來改變 Session ID:
...
@WebListener()
public class SomeContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.getSessionCookieConfig().setName("caterpillar-sessionId");
}
}
若想透過 ServletContext
來動態部署 Servlet,底下是個例子:
package cc.openhome;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;
@WebListener
public class ResourceServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
ServletRegistration.Dynamic servlet = ctx.addServlet("HelloWorld", new HelloWorld());
servlet.setLoadOnStartup(1);
servlet.addMapping("/helloworld");
}
}
在上例中,HelloWorld
類別是個簡單的 Servlet,不需要使用 @WebServlet
標註。
如果你想要物件被設置、移除或替換 ServletContext
屬性時,可以收到通知以進行一些動作,則可以實作 ServletContextAttributeListener
。
package javax.servlet;
import java.util.EventListener;
public interface ServletContextAttributeListener extends EventListener {
public default void attributeAdded(ServletContextAttributeEvent scae) {}
public default void attributeRemoved(ServletContextAttributeEvent scae) {}
public default void attributeReplaced(ServletContextAttributeEvent scae) {}
}
當你在 ServletContext
中加入屬性、移除屬性或替換屬性時,相對應的 attributeAdded()
、attributeRemoved()
與 attributeReplaced()
方法就會被呼叫。
如果希望容器在部署應用程式時,實例化實作 ServletContextAttributeListener
的類別並註冊給應用程式,則同樣也是在實作類別上標註 @WebListener
:
package cc.openhome;
...
@WebListener()
public class SomeContextAttrListener
implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scab) {
//...
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scab) {
//...
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scab) {
//...
}
}
另一個方式是在 web.xml 下如下設定:
<web-app...>
...
<listener>
<listener-class>cc.openhome.SomeContextAttrListener</listener-class>
</listener>
...
<web-app>