Servlet3.0 提供了 @WebServlet
,@WebFilter
等註解,這樣便有了拋棄 web.xml
的第一個途徑,憑藉註解宣告 Servlet 和 Filter 來做到這一點。
除了這種方式,Servlet3.0 規範還提供了更強大的功能,可以在執行時動態註冊 Servlet ,Filter,listener。以 Servlet 為例,過濾器與監聽器與之類似。ServletContext 為動態配置 Servlet 增加了如下方法:
ServletRegistration.Dynamic addServlet(String servletName,Class<? extends Servlet> servletClass)
ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
ServletRegistration.Dynamic addServlet(String servletName, String className)
T createServlet(Class clazz)
ServletRegistration getServletRegistration(String servletName)
Map<String,? extends ServletRegistration> getServletRegistrations()
Map<String,? extends ServletRegistration> getServletRegistrations()
其中前三個方法的作用是相同的,只是引數型別不同而已;
透過 #createServlet(Class clazz)
方法建立的 Servlet,通常需要做一些自定義的配置,然後使用 #addServlet(...)
方法來將其動態註冊為一個可以用於服務的 Servlet。
兩個 #getServletRegistration()
方法主要用於動態為 Servlet 增加對映資訊,這等價於在 web.xml
中使用 標籤為存在的 Servlet 增加對映資訊。
以上新增的方法一般在如下情況呼叫:
javax.servlet.ServletContextListener
的#contextInitialized(ServletContextEvent sce)
方法中呼叫。javax.servlet.ServletContainerInitializer
的#onStartup(Set<Class<?>> c, ServletContext ctx)
方法中呼叫。
ServletContainerInitializer 也是 Servlet 3.0 新增的一個介面,容器在啟動時使用 JAR 服務 API(JAR Service API) 來發現 ServletContainerInitializer 的實現類,並且容器將 WEB-INF/lib
目錄下 JAR 包中的類都交給該類的 #onStartup(...)
方法處理,我們通常需要在該實現類上使用 @HandlesTypes
註解來指定希望被處理的類,過濾掉不希望給 #onStartup(...)
處理的類。
將Servlet-HelloWorld配置的web.xml
去掉,編寫如下類
package com.lzc.servlet;
import javax.servlet.*;
import java.util.Set;
/**
* Created by lzc
* 2020/2/12 21:39
*/
public class CustomServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("CustomServletContainerInitializer create ServletHelloWorld ...");
ServletRegistration.Dynamic helloServlet = servletContext.addServlet("HelloWorld", ServletHelloWorld.class);
helloServlet.addMapping("/HelloWorld");
}
}
ServletContext 我們稱之為 servlet 上下文,它維護了整個 web 容器中註冊的 servlet,filter,listener,以 servlet 為例,可以使用 servletContext.addServlet
等方法來新增 servlet。
這麼宣告一個 ServletContainerInitializer 的實現類,web 容器並不會識別它,所以,需要藉助 SPI 機制來指定該初始化類,這一步驟是透過在專案的 resources 路徑下建立 META-INF/services/javax.servlet.ServletContainerInitializer
來做到的,它只包含一行內容:
com.lzc.servlet.CustomServletContainerInitializer
使用 ServletContainerInitializer 和 SPI 機制,我們的 web 應用便可以徹底擺脫 web.xml
了。
方法入參中 Set<Class<?>> set
和 @HandlesTypes
註解的使用
首先編寫一個介面和該介面的實現類
public interface Hello {
void hello();
}
public class HelloWorld implements Hello {
@Override
public void hello() {
System.out.println("HelloWorld...");
}
}
改造 CustomServletContainerInitializer
@HandlesTypes({Hello.class})
public class CustomServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
for (Class<?> cls : set) {
try {
Hello hello = (Hello)cls.newInstance();
hello.hello();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println("CustomServletContainerInitializer create ServletHelloWorld ...");
ServletRegistration.Dynamic demoServlet = servletContext.addServlet("HelloWorld", ServletHelloWorld.class);
demoServlet.addMapping("/HelloWorld");
}
}
執行上面程式碼即可看到效果
@HandlesTypes指定需要處理的類,Set<Class<?>> set攜帶了所有@HandlesTypes指定的類
本作品採用《CC 協議》,轉載必須註明作者和本文連結