動態加載
動態加載是 Servlet 3.0 中的新特性,它可以實現在不重啟 Web 應用的情況下加載新的 Web 對象(Servlet、Filter、Listener)。
為了實現動態加載的第一種方式:創建 Web 對象和注冊 Web 對象到 ServletContext 中分步進行
ServletContext 接口增加了如下方法,用於動態創建 Web 對象:
<T extends Servlet> T createServlet(java.lang.Class<T> c) throws ServletException // <T extends Filter> T createFilter(java.lang.Class<T> c) throws ServletException // <T extends java.util.EventListener> T createListener(java.lang.Class<T> c) throws ServletException //
例如,如果MyServlet是一個直接或者間接繼承 javax.servlet.Servlet 的類,那么就可以通過 createServlet 的方法初始化它:
Servlet myServlet = servletContext.createServlet(MyServlet.class); // 這里使用了反射技術
在創建了 Web 對象之后,可以通過 ServletContext 中的如下方法把它注冊到 ServletContext 中
ServletRegistration.Dynamic addServlet(java.lang.String servletName, Servlet servlet) //
FilterRegistration.Dynamic addFilter(java.lang.String filterName, Filter filter
) //
<T extends java.util.EventListener> void addListener(T t) //
實現動態加載的第二種方式:創建 Web 對象和注冊 Web 對象到 ServletContext 中一步完成
使用 ServletContext 中的如下方法
ServletRegistration.Dynamic addServlet(java.lang.String servletName, java.lang.Class<? extends Servlet> servletClass) ServletRegistration.Dynamic addServlet(java.lang.String servletName, java.lang.String className
) FilterRegistration.Dynamic addFilter(java.lang.String filterName, java.lang.Class<? extends Filter> filterClass
) FilterRegistration.Dynamic addFilter(java.lang.String filterName, java.lang.String className
) void addListener(java.lang.Class<? extends java.util.EventListener> listenerClass
) void addListener(java.lang.String className
)
要創建或者增加Listener,傳遞給第一個 addListener 方法的類需要實現以下的一個或者多個接口
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
HttpSessionAttributeListener
如果 ServletContext 是用於 ServletContextInitializer 中的 onStartup 方法的參數,那么 Listener 也需要實現 ServletContextListener
動態加載實例
package app14a.servlet; 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; public class FirstServlet extends HttpServlet { // 一個 Servlet,該 Servlet 沒有使用 @WebServlet 注解,也沒有使用部署描述符來聲明。 private static final long serialVersionUID = 1L; // 而是通過使用 Listener 來動態創建、注冊、綁定這個 Servlet 並讓其生效的。 private String name; public void setName(String name) { this.name = name; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>First Servlet</title>"); writer.println("</head>"); writer.println("<body>"); writer.println(name); writer.println("</body>"); writer.println("</html>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package app14a.listener; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletRegistration; import javax.servlet.annotation.WebListener; import app14a.servlet.FirstServlet; @WebListener // 注解 public class DynRegListener implements ServletContextListener { // 監聽器 @Override public void contextInitialized(ServletContextEvent sce) { // ServletContext 創建時,容器調用該方法 ServletContext servletContext = sce.getServletContext(); // 獲取 ServletContext 對象實例 Servlet firstServlet = null; try { firstServlet = servletContext.createServlet(FirstServlet.class); // 動態創建 Web 對象 } catch (Exception e) { e.printStackTrace(); } if (firstServlet != null && firstServlet instanceof FirstServlet) { ((FirstServlet) firstServlet).setName("Dynamically registered servlet"); } ServletRegistration.Dynamic dynamic = servletContext.addServlet("firstServlet", firstServlet); // 注冊到 ServletContext 中 dynamic.addMapping("/dynamic"); // 綁定路徑 } @Override public void contextDestroyed(ServletContextEvent sce) { } }
測試結果,Servlet 成功注冊到了 ServletContext 中,且綁定到 /dynamic 路徑
Servlet容器加載
Servlet 容器加載器也是 Servlet 3.0 中的新特性,對於框架的開發者來說特別有用。
Servlet 容器初始化主要是通過 javax.servlet.ServletContainerInitializer 這個接口。該接口只有一個方法 onStartup。
Servlet 容器中,這個方法在任何 ServletContext 的 Listener 初始化之前都可能會被調用到。
void onStartup(java.util.Set<java.lang.Class<?>> c, ServletContext ctx) throws ServletException //
ServletContainerInitializer 的實現類必須使用 HandleTypes 的注解,以便讓加載器能夠識別。
package app14a.initializer; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.annotation.HandlesTypes; import servlet.UsefulServlet; @HandlesTypes({UsefulServlet.class}) // ServletContainerInitializer 的實現類必須使用該注解 public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException { // 該方法的主要任務就是注冊 Web 對象 System.out.println("onStartup"); ServletRegistration registration = servletContext.addServlet("usefulServlet", "servlet.UsefulServlet"); // 創建並注冊 registration.addMapping("/useful"); // 綁定訪問路徑 System.out.println("leaving onStartup"); } }
插件(即 initializer.jar)中的內容
測試結果,