使用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容器啓動。