我们在web服务中是怎么启动Spring容器的,我们通常是在web.xml中配置了一个ContextLoaderListener的监听器,然后设置contextConfigLocation,
1、ContextLoaderListener这个ContextLoaderListener监听器继承了ContextLoader实现了ServletContextListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
所以当web容器启动后就会被监听到,然后调用contextInitialized方法,进而执行initWebApplicationContext方法。然而这个方法是继承于ContextLoader这个父类,一看这个类的名字就知道这就是一个上下文的加载器。
2、ContextLoader接下来具体看下initWebApplicationContext方法的实现。而且容器的实际初始化工作是从这里开始的。
总的来说,就是干了下面几件事:
1、初始化之前检查当前ServletContext是否已经存在Spring容器;
2、使用无参构造(Java类)或者主要构造(Kotlin类)实例化1个ConfigurableWebApplicationContext实现类,默认是XMLWebApplicationContext;
3、为已经实例化的ConfigurableWebApplicationContext设置父容器,通常为null,除非自定义子类复写loadParentContext。
4、配置并刷新ConfigurableWebApplicationContext(重点);
5、以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key存储在当前ServletContext中;
6、将ConfigurableWebApplicationContext存储在当前ContextLoader内部。
这里面最重要的方法是configureAndRefreshWebApplicationContext(cwac, servletContext) ,负责为ConfigurableWebApplicationContext设置一些属性为接下来的refresh做准备
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();
}
可以看到先是为ConfigurableWebApplicationContext设置了一些属性,然后开始refresh容器
1、设置ConfigurableWebApplicationContext的unique id;
2、调用wac.setServletContext(sc),设置ServletContext为当前ServletContext;
3、获取web.xml中contextConfigLocation配置信息,并调用wac.setConfigLocation(configLocationParam)进行设置(重要)
4、提前初始化PropertySources以便在refresh前执行一些后处理或者初始化工作。
5、自定义一些容器配置,设置了ConfigLocation之后,refresh之前执行
6、上面工作完成后,开始refresh容器(很重要!很重要!很重要!)
属性设置完成,要刷新容器了,我们进入AbstractApplicationContext的refresh() 来一探究竟!
3、 AbstractApplicationContext首先看下refresh()方法实现,这是Spring容器初始化最核心的方法,前面都只是准备工作,不管以哪种方式启动Spring容器,最终必定会调用这个方法。限于篇幅原因,本篇博客只是大致介绍refresh方法各个流程,让大家先有个印象,具体每个流程的细节将会在后续博客陆续阐述。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}



