- 在 Servlet 3.0 環境中,服務器的 Servlet 容器會在類路徑中查找實現 javax.servlet.ServletContainerInitializer 接口的類,如果能發現的話,就調用它的 onStartup(*)
- 這個實現類一般不需要我們寫,因爲 Spring 已經提供了:SpringServletContainerInitializer
//javax.servlet.annotation.HandlesTypes 指定那些需要處理的類型;
//指定了 WebApplicationInitializer 接口,所有實現了該接口的類型都會被收集到下面 onStartup() 的第一個參數
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//WebApplicationInitializer 實現類類型都被注入到第一個參數,包括我們自定義的實現類
//Spring 會依次調用這些類的 onStartup(ServletContext servletContext) ,無論我們定義了多少個
//ServletContext 是服務器的容器,可以向它註冊 Listener 和 Servlet
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
由此,我們需要新建一個類實現 WebApplicationInitializer 接口,並重寫 onStartup(ServletContext servletContext) ,然後在裏邊分別創建 Spring 的 IOC 容器和 WebMVC 容器,並且向 ServletContext(服務器的容器) 註冊 Spring 的 ContextLoaderListener 和 DispatcherServlet
但是!Spring 有一系列的實現類和子類幫我們做了很多工作,來看 Spring 提供的 WebApplicationInitializer 實現類:AbstractContextLoaderInitializer
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
//......
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
//創建一個 ContextLoaderListener
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
//向 Servlet 容器註冊 ContextLoaderListener
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
//抽象模板方法,留給子類重寫:創建 IOC 容器
protected abstract WebApplicationContext createRootApplicationContext();
//如果子類不重寫,無任何參數提供給 IOC 容器
protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
return null;
}
}
可以看得出來,AbstractContextLoaderInitializer 的貢獻就是定義了一套標準的創建 ContextLoaderInitializer 的流程,並以模板方法模式將細節定義開放給子類,但是這還沒完!Spring 進一步提供了它的子類:AbstractDispatcherServletInitializer
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
//......
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//父類創建 Listener 的流程要走
super.onStartup(servletContext);
//自己創建 Servlet 的流程也要走
registerDispatcherServlet(servletContext);
}
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return empty or null");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
//創建一個 DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//向 Servlet 容器註冊 DispatcherServlet
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
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);
}
//......
//抽象模板方法,留給子類重寫:創建 WebMVC 容器
protected abstract WebApplicationContext createServletApplicationContext();
//......
//如果子類不重寫,無任何參數提供給 WebMVC 容器
protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
return null;
}
//抽象模板方法,留給子類重寫:設置 DispatcherServlet URL映射
protected abstract String[] getServletMappings();
//子類可重寫:可以提供多個 Filter,Spring 會將他們一一註冊到服務器的 Servlet 容器中
protected Filter[] getServletFilters() {
return null;
}
//向 Servlet 容器註冊 Filter
protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
String filterName = Conventions.getVariableName(filter);
Dynamic registration = servletContext.addFilter(filterName, filter);
if (registration == null) {
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?");
}
}
registration.setAsyncSupported(isAsyncSupported());
registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
return registration;
}
//......
//子類可重寫:自定義設置 DispatcherServlet 的註冊表信息,比如設置初始化參數
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
}
}
一樣可以看得,AbstractDispatcherServletInitializer 的貢獻是定義了一套標準的創建 DispatcherServlet 的流程,並以模板方法模式將細節定義開放給子類,然而,這還沒完!Spring 依舊提供了它的子類:AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
//創建 AnnotationConfigWebApplicationContext 類型的 IOC 容器
@Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}
//創建 AnnotationConfigWebApplicationContext 類型的 WebMVC 容器
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
servletAppContext.register(configClasses);
}
return servletAppContext;
}
//抽象模板方法,留給子類重寫:提供 IOC 容器需要的 JavaConfig
protected abstract Class<?>[] getRootConfigClasses();
//抽象模板方法,留給子類重寫:提供 WebMVC 容器需要的 JavaConfig
protected abstract Class<?>[] getServletConfigClasses();
}
看到這裏已經很清楚了,這個類將兩個 Spring 容器的創建再度簡化,簡單到我們只需要提供 JavaConfig!所以我們開發者要做的就是,新建一個類繼承 AbstractAnnotationConfigDispatcherServletInitializer,並重寫以上幾個類的抽象方法,以下是一個簡單例子
public class GoWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
// IOC 容器配置
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
// WebMvc 容器配置
return new Class<?>[] { WebMvcConfig.class };
}
@Override
protected String[] getServletMappings() {
// DispatcherServlet URL映射
return new String[] { "/" };
}
}