動態加載及Servlet容器加載


動態加載

動態加載是 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)中的內容

測試結果,

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM