基於4.1.7.RELEASE
Spring-WebApplicationInitializer
AbstractContextLoaderInitializer / AbstractDispatcherServletInitializer / AbstractAnnotationConfigDispatcherServletInitializer
後3個類都是實現了WebApplicationInitializer接口,WebApplicationInitializer接口主要的作用是提供在Servlet 3.0+環境中對於ServletContext的可編程實現,可以跟web.xml混合使用,該接口的實現類會被SpringServletContainerInitializer自動調用,而SpringServletContainerInitializer則會被任意3.0+的Servlet自動啓用,可以使用@Order註解來定義WebApplicationInitializer的執行順序。
值得注意的一點是在tomcat的7.0.14(包含)以下版本時, tomcat會把url :“/”跟DefaultServlet綁定在一起且無法重寫綁定關係。
需要注意的方法:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
在初始化Web Application的時候被調用,用於配置相關的ServletContext
在AbstractContextLoaderInitializer裏,這裏調用了registerContextLoaderListener方法,向其子類提供的WebApplicationContext註冊了一個ContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext));
}
}
而在AbstractDispatcherServletInitializer類中,onStartup則被重寫,調用了本類的registerDispatcherServlet方法,註冊了一個默認名字爲dispatcher的DispatcherServlet,並且對Servlet進行了一些初始化工作。
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
WebApplicationContext servletAppContext = createServletApplicationContext();
DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
由代碼中可以看出,在註冊完dispatchServlet之後,設置啓動優先級爲1表明該Servlet隨容器啓動而初始化,添加Servlet的URL mapping即web.xml中的url-pattern選項,註冊Servlet的filters並且調用了customizeRegistration方法來實現自定義配置ServletContext。其中
getServletName(), customizeRegistration() 可由實現類自己決定是否重寫
createServletApplicationContext(),createRootApplicationContext()(繼承自AbstractContextLoaderInitializer),getServletMappings()子類必須實現
有一個有意思的現象是在registerServletFilter中添加Filter時
int counter = -1;
while (counter == -1 || registration == null) {
counter++;
registration = servletContext.addFilter(filterName + "#" + counter, filter);
Assert.isTrue(counter < 100,
"Failed to register filter '" + filter + "'." +
"Could the same Filter instance have been registered already?");
}
這表明,同一個名稱的filter最多隻能註冊100個。
由於AbstractAnnotationConfigDispatcherServletInitializer繼承了AbstractDispatcherServletInitializer,所以它滿足了上面要求的createServletApplicationContext(),createRootApplicationContext()兩個方法實現,而由於它是一個抽象類,getServletMappings()則被交給它的子類去實現。這兩個方法的實現大同小異
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
}else {
return null;
}
如上所示,其不同在意獲取configClasses的不同之處,一個是獲取用於Servlet Application Context的配置,另一個是用於root Application Context的配置,無論是用在哪裏,都是由其子類通過註解@Configuration和@Component來標識的。