使用Spring MVC的时候,每次都要在web.xml中初始化一个DispatcherServlet,这是为什么呢?因为我们需要DispatcherServlet来将Spring容器启动起来,启动完成后,由DispatcherServlet来接收请求并分发。
首先。DispatcherServlet 继承 FrameworkServlet,FrameworkServlet 继承 HttpServletBean ,HttpServletBean 继承 HttpServlet ,即原生的 HttpServlet 。
我们下面看一下DispatcherServlet的初始化过程:
系统new了 DispatcherServlet 了之后,会调用其init() 方法,即 HttpServletBean 的init方法:
@Override
public final void init() throws ServletException {
// 从配置文件中读取出配置,比如在web.xml中读取的 name为contextConfigLocation,value为 classpath*:servlet-context.xml
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// 此方法非常非常重要,初始化容器,与web中的相关映射。
initServletBean();
}
继续看 FrameworkServlet中的 initServletBean() 方法:
protected final void initServletBean() throws ServletException {
long startTime = System.currentTimeMillis();
try {
// 此方法非常重要,初始化了一个 ApplicationContext 容器,完成了beanDefinition的生成,根据beanDefinition生成bean的操作
// 在bean生成完成之后,此方法还会初始化 mvc 中的 HanderMapping、handerAdapter等一系列组件
this.webApplicationContext = initWebApplicationContext();
}
. . .
if (this.logger.isInfoEnabled()) {
// 还会计时,我们平常的计时就在这里来完成的
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
再看一下 initWebApplicationContext() 方法:
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
. . .
if (wac == null) {
// 此为创建一个 WebApplicationContext ,并调用其 refresh() 方法来生成容器
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 此为调用自身的 onRefresh() 方法,来初始化 mvc 中的 HanderMapping、handerAdapter等一系列组件
onRefresh(wac);
}
. . .
return wac;
}
首先看一下 createWebApplicationContext() ,看其实如何来生成一个 容器 的:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
// 先使用 BeanUtils 生成了一个 ApplicationContext,注意我们生成的是 XmlWebApplicationContext
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
// 此为将 context 文件的路径注入进去,比如我们之前使用的xmlClassPathApplicationContext,就需要一个 xml 文件的路径,现在也是一样的,通过这个将其注入进去
wac.setConfigLocation(getContextConfigLocation());
// 最后调用方法将其初始化完成
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
. . .
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
// 注意,最后非常重要!调用了 applicationContxt的 refresh() 方法,先根据ConfigLocation得到Resource,在根据Resource生成beanDefinition,最后根据 beanDefinition 生成bean。
wac.refresh();
}
至此,Spring容器启动。