Spring中WebApplicationInitializer的理解
現在JavaConfig配置方式在逐步取代xml配置方式。而WebApplicationInitializer可以看做是Web.xml的替代,它是一個介面。通過實現WebApplicationInitializer,在其中可以新增servlet,listener等,在載入Web專案的時候會載入這個介面實現類,從而起到web.xml相同的作用。下面就看一下這個介面的詳細內容。
首先開啟這個介面,如下:
public interface WebApplicationInitializer { void onStartup(ServletContext var1) throws ServletException; }
只有一個方法,看不出什麼頭緒。但是,在這個包下有另外一個類,SpringServletContainerInitializer。它的實現如下:
package org.springframework.web; import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.HandlesTypes; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @HandlesTypes({WebApplicationInitializer.class}) public class SpringServletContainerInitializer implements ServletContainerInitializer { public SpringServletContainerInitializer() { } public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList(); Iterator var4; if(webAppInitializerClasses != null) { var4 = webAppInitializerClasses.iterator(); while(var4.hasNext()) { Class<?> waiClass = (Class)var4.next(); if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer)waiClass.newInstance()); } catch (Throwable var7) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7); } } } } if(initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); } else { servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); var4 = initializers.iterator(); while(var4.hasNext()) { WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next(); initializer.onStartup(servletContext); } } } }
這個類就比較有意思了,先不管其他的,讀一下這段程式碼,可以得到這樣的意思。
先判斷webAppInitializerClasses這個Set是否為空。如果不為空的話,找到這個set中不是介面,不是抽象類,並且是
WebApplicationInitializer介面實現類的類,將它們儲存到list中。當這個list為空的時候,丟擲異常。不為空的話就按照一定的順序排序,並將它們按照一定的順序例項化。呼叫其onStartup方法執行。到這裡,就可以解釋WebApplicationInitializer實現類的工作過程了。但是,在web專案執行的時候,SpringServletContainerInitializer這個類又是怎樣被呼叫的呢。
它只有一個介面,ServletContainerInitializer,通過它就可以解釋SpringServletContainerInitializer是如何被呼叫的。它的內容如下,
package javax.servlet;
import java.util.Set;
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
首先,這個介面是javax.servlet下的。官方的解釋是這樣的:為了支援可以不使用web.xml。提供了ServletContainerInitializer,它可以通過SPI機制,當啟動web容器的時候,會自動到新增的相應jar包下找到META-INF/services下以ServletContainerInitializer的全路徑名稱命名的檔案,它的內容為ServletContainerInitializer實現類的全路徑,將它們例項化。既然這樣的話,那麼SpringServletContainerInitializer作為ServletContainerInitializer的實現類,它的jar包下也應該有相應的檔案。開啟檢視如下:
哈,現在就可以解釋清楚了。首先,SpringServletContainerInitializer作為ServletContainerInitializer的實現類,通過SPI機制,在web容器載入的時候會自動的被呼叫。(這個類上還有一個註解@HandlesTypes,它的作用是將感興趣的一些類注入到ServletContainerInitializerde), 而這個類的方法又會掃描找到WebApplicationInitializer的實現類,呼叫它的onStartup方法,從而起到啟動web.xml相同的作用。
然後,我們自己通過一個例項來實現相同的功能,通過一樣的方式來訪問一個servlet。
1、定義介面WebParameter,它就相當於WebApplicationInitializer。內容如下:
public interface WebParameter {
void loadOnstarp(ServletContext servletContext);
}
可以在這裡面新增servlet,listener等。
2、定義Servlet。
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("TestSetvlet");
}
}
3、定義MyWebParameter作為WebParameter的實現類,將Servlet新增到上下文,並設定好對映。
public class MyWebParameter implements WebParameter {
public void loadOnstarp(ServletContext servletContext) {
ServletRegistration.Dynamic testSetvelt=servletContext.addServlet("test","com.test.servlet.MyServlet");
testSetvelt.setLoadOnStartup(1);
testSetvelt.addMapping("/test");
}
}
4、定義好WebConfig作為ServletContainerInitializer的實現類,它的作用是掃描找到WebParameter的實現類,並呼叫其方法。
@HandlesTypes({WebParameter.class})
public class WebConfig implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
Iterator var4;
if (set!=null){
var4=set.iterator();
while(var4.hasNext()){
Class<?> clazz= (Class<?>) var4.next();
if (!clazz.isInterface()&& !Modifier.isAbstract(clazz.getModifiers())&&WebParameter.class.isAssignableFrom(clazz)){
try {
((WebParameter) clazz.newInstance()).loadOnstarp(servletContext);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
}
5、根據SPI機制,定義一個META-INF/services資料夾,並在其下定義相關檔名稱,並將WebConfig的類全名稱填入其中。
至此,相關內容就完成了,因為我用的maven,通過install將其作為jar包上傳到本地倉庫。從另外一個web專案呼叫這個包進行訪問。
6、最終結果:
相關程式碼請轉至我的github點選開啟連結
ps:其中涉及到了SPI相關的內容,如果不懂請自行百度。如果認識有誤,請大佬指出,謝謝。