- 在 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[] { "/" };
}
}