Spring与MVC(二)

在上一篇文章Spring与MVC(一)中,我们配置了一个最基本的Web应用,这一章我们来看看这个最基本的Web应用是如何创建DispatcherServletContextLoaderListener的。

SpringServletContainerInitializer

在Servlet 3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话,就会用它来配置Servlet容器。

Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring 3.2引入了一个便利的WebApplicationInitializer基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer。因为我们扩展了AbstractAnnotationConfigDispatcherServletInitializer(同时也就是实现了WebApplicationInitializer),因此当部署到Servlet 3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。

SpringServletContainerInitializer实现了ServletContainerInitializer接口唯一的一个方法:onStartup。在该方法中,寻找所有实现了WebApplicationInitializer接口的配置类,然后根据@Order注解指定的顺序依次调用我们配置类的onStartup方法。

AbstractAnnotationConfigDispatcherServletInitializer的基础关系:

AbstractAnnotationConfigDispatcherServletInitialize

调用AbstractDispatcherServletInitializer.onStartup方法:

1
2
3
4
5
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}

创建ContextLoaderListener

首先调用父类AbstractContextLoaderInitializeronStartup方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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");
}
}

首先调用AbstractAnnotationConfigDispatcherServletInitializer.createRootApplicationContext创建一个AnnotationConfigWebApplicationContext

1
2
3
4
5
6
7
8
9
10
11
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}

其中getRootConfigClasses方法我们在WebAppInitializer重写了,返回的是RootConfig.class。接着新建一个AnnotationConfigWebApplicationContext,将RootConfig.class注册到AnnotationConfigWebApplicationContextannotatedClasses中。

回到AbstractContextLoaderInitializer.registerContextLoaderListener,创建一个ContextLoaderListener,将createRootApplicationContext返回的AnnotationConfigWebApplicationContext赋值给ContextLoaderListener的context中。

将刚才创建的ContextLoaderListener添加到ServletContext中。

创建DispatcherServlet

回到AbstractDispatcherServletInitializer.onStartup,创建完ContextLoaderListener之后,调用registerDispatcherServlet注册DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);
}

首先调用getServletName()方法,获取servletName,默认为”dispatcher”。

调用AbstractAnnotationConfigDispatcherServletInitializer.createServletApplicationContext,创建AnnotationConfigWebApplicationContext

1
2
3
4
5
6
7
8
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
servletAppContext.register(configClasses);
}
return servletAppContext;
}

首先创建AnnotationConfigWebApplicationContext。再调用getServletConfigClasses方法获取配置,该方法在WebAppInitializer中被重写,返回WebConfig.class。然后注册到AnnotationConfigWebApplicationContext的annotatedClasses中。

回到AbstractDispatcherServletInitializer.registerDispatcherServlet方法,调用createDispatcherServlet创建DispatcherServlet

将我们创建的DispatcherServlet添加到servletContext中。在registration中添加Servlet映射,Servlet映射由我们的WebAppInitializer配置类指定。

获取过滤器,注册到servletContext中。

总结

我们可以看到在AbstractDispatcherServletInitializer.onStartup中:

  • 通过super.onStartup(servletContext)方法创建ContextLoaderListenergetRootConfigClasses方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean。
  • 通过registerDispatcherServlet(servletContext)方法创建DispatcherServletgetServletConfigClasses方法返回的带有@Configuration注解的类将会用来配置DispatcherServlet应用上下文中的bean。