public WebApplicationContext initWebApplicationContext(ServletContext servletContext){
if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)!= null){
thrownewIllegalStateException("Cannot initialize context because there is already a root application context present - "+"check whether you have multiple ContextLoader* definitions in your web.xml!");}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");if(logger.isInfoEnabled()){
logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try{
// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.if(this.context == null){
//创建WebApplicationContext对象,//如果在xml中配置了参数contextClass的话,取参数值定义的class//如果xml未配置参数,那么读取spring配置文件ContextLoader.properties定义的class//org.springframework.web.context.support.XmlWebApplicationContext//XmlWebApplicationContext实现了WebApplicationContext接口this.context =createWebApplicationContext(servletContext);}if(this.context instanceofConfigurableWebApplicationContext){
//强转成ConfigurableWebApplicationContext
ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext)this.context;//容器未启动if(!cwac.isActive()){
// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif(cwac.getParent()== null){
// The context instance was injected without an explicit parent ->// determine parent for root web application context, if any.// 这里spring5之后已经默认返回null
ApplicationContext parent =loadParentContext(servletContext);
cwac.setParent(parent);}//配置并刷新容器,这里读取了xml配置中的spring配置文件名称configureAndRefreshWebApplicationContext(cwac, servletContext);}}//把整个容器信息放入到servletContext属性中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();if(ccl == ContextLoader.class.getClassLoader()){
currentContext =this.context;}elseif(ccl != null){
currentContextPerThread.put(ccl,this.context);}if(logger.isDebugEnabled()){
logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["+
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE +"]");}if(logger.isInfoEnabled()){
long elapsedTime = System.currentTimeMillis()- startTime;
logger.info("Root WebApplicationContext: initialization completed in "+ elapsedTime +" ms");}returnthis.context;}catch(RuntimeException ex){
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}catch(Error err){
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);throw err;}}
2 配置并刷新容器
//配置并刷新容器protectedvoidconfigureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc){
//这里setIDif(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()));}}//让容器持有ServletContext,ServletContext和容器相互持有对方对象
wac.setServletContext(sc);//读取了xml配置中的spring配置文件名称//例如//<context-param>// <param-name>contextConfigLocation</param-name>// <param-value>classpath*:applicationContext-aop-annotation.xml</param-value>// </context-param>
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if(configLocationParam != null){
wac.setConfigLocation(configLocationParam);}// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();if(env instanceofConfigurableWebEnvironment){
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}//所有实现了ApplicationContextInitializer接口的类都会在这一步被调用//可以通过web.xml参数化形式globalInitializerClasses和contextInitializerClasses配置//也可以通过spi来配置,//如果是springboot,也可以main函数启动容器时添加customizeContext(sc, wac);//真正刷新容器,加载类信息
wac.refresh();}
2.1 refresh
@Overridepublicvoidrefresh()throws BeansException, IllegalStateException {
//加锁串行synchronized(this.startupShutdownMonitor){
// Prepare this context for refreshing.//准备阶段,更新容器启动时间,启动状态,读取配置在jvm或者系统中的配置文件然后验证prepareRefresh();// Tell the subclass to refresh the internal bean factory.// 告诉子类刷新和初始化 beanFactory DefaultListableBe