传统的使用SpringMVC的方法是在web.xml中配置DispatchServlet,在SpringMVC 3.1版本后提供了一种新的启动方式。在介绍这种新的方式之前,有必要先介绍一下Servlet 3.0的一个新特性 ServletContainerInitializer 。先看一下javadoc对该接口的介绍

实现该接口的类会成为web应用的入口,并且在启动期间可以注入servlets,filters,和listeners。

该接口的实现类可能会包含 **javax.servlet.annotation.HandlesTypes** 注解,该注解的value对应的Class<?> [], 用于表示该接口的实现类的onStartup方法会接收Class<?> []对应的实现或者子类作为参数。

如果该接口的实现类不包含 **javax.servlet.annotation.HandlesTypes** 注解,或者应用中没有该注解中对应的Class,则需要传递null给onStartup方法。

如果实现了该接口则需要在jar的META-INF/services目录下放置一个文件,改文件的名称必须是该接口的全名称。

从上面javadoc的注释可以看出,当应用启动的时候,如果有 ServletContainerInitializer 的实现类并且有相应的配置,则会使用该类作为web的入口。

SpringMVC新的启动方式正是基于上面提到的 ServletContainerInitializer 接口,其实现类是 SpringServletContainerInitializer ,下面看看该类的实现,

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@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(servleContext);
		}
	}

}

上面的代码功能比较明确,针对每一个传入的 WebApplicationInitializer 的实现类进行实例化,然后将这些实例化的对象进行排序操作。排序的规则是

如果类带有 org.springframework.core.annotation.Order 标签,则获取对应的Order值,如果类实现 org.springframework.core.Ordered 接口,则调用getOrder方法获取Order值,如果都没有则为最低Order值。获取所有实例的Order值后,根据Order值进行排序。

排序好后,依次调用实例的onStartup方法进行初始化。

SpringServletContainerInitialize 作为SpringMVC初始化的入口,最终的实现依赖于 WebApplicationInitializer 的实现类 AbstractAnnotationConfigDispatcherServletInitializer

public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {

	/**
	 * {@inheritDoc}
	 * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
	 * providing it the annotated classes returned by {@link #getRootConfigClasses()}.
	 * Returns {@code null} if {@link #getRootConfigClasses()} returns {@code null}.
	 */
	@Override
	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
			rootAppContext.register(configClasses);
			return rootAppContext;
		}
		else {
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
	 * providing it the annotated classes returned by {@link #getServletConfigClasses()}.
	 */
	@Override
	protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			servletAppContext.register(configClasses);
		}
		return servletAppContext;
	}

	/**
	 * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
	 * and/or {@link org.springframework.stereotype.Component @Component} classes to be
	 * provided to the {@linkplain #createRootApplicationContext() root application context}.
	 * @return the configuration classes for the root application context, or {@code null}
	 * if creation and registration of a root context is not desired
	 */
	protected abstract Class<?>[] getRootConfigClasses();

	/**
	 * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
	 * and/or {@link org.springframework.stereotype.Component @Component} classes to be
	 * provided to the {@linkplain #createServletApplicationContext() dispatcher servlet
	 * application context}.
	 * @return the configuration classes for the dispatcher servlet application context or
	 * {@code null} if all configuration is specified through root config classes.
	 */
	protected abstract Class<?>[] getServletConfigClasses();

}

该类提供了两个抽象方法 getRootConfigClassesgetServletConfigClasses ,前者用于获取创建RootApplicationContext的配置,后者用于获取创建ServletApplicationContext的配置。另外两个方法 createRootApplicationContextcreateServletApplicationContext 用于创建RootApplicationContext和ServletApplicationContext。这个类的入口onStartup在其父类 AbstractDispatcherServletInitializer 中。

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		registerDispatcherServlet(servletContext);
	}

onStartup会首先调用其父类 AbstractContextLoaderInitializer 的onStartup方法进行初始化,

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	protected void registerContextLoaderListener(ServletContext servletContext) {
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
		else {
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

AbstractContextLoaderInitializer 的onStartup主要用于初始化RootApplicationContext。在RootApplicationContext初始化好后,AbstractDispatcherServletInitializer 会继续初始化DispatcherServlet。

	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 + "]");

		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

		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);
	}

registerDispatcherServlet 调用 createServletApplicationContext 创建ServletApplicationContext,然后创建DispatcherServlet,设置ServletMapping,然后注册Filter。

经过上面的流程,SpringMVC的加载过程就完成了后续的请求处理就和通过web.xml启动的SpringMVC一样了。