如上,我们看一下上面的创建过程。首先 Spring 会检测 ServletContext 中 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 属性有没有被设置,若被设置过,则抛出异常。若未设置,则调用 createWebApplicationContext 方法创建容器。创建好后,再调用 configureAndRefreshWebApplicationContext 方法配置并刷新容器。最后,调用 setAttribute 方法将容器设置到 ServletContext 中。经过以上几步,整个创建流程就结束了。流程并不复杂,可简单总结为创建容器 → 配置并刷新容器 → 设置容器到 ServletContext 中。这三步流程中,最后一步就不进行分析,接下来分析一下第一步和第二步流程对应的源码。如下:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 判断创建什么类型的容器,默认类型为 XmlWebApplicationContext Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } // 通过反射创建容器 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } protected Class<?> determineContextClass(ServletContext servletContext) { /* * 读取用户自定义配置,比如: * <context-param> * <param-name>contextClass</param-name> * <param-value>XXXConfigWebApplicationContext</param-value> * </context-param> */ String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { /* * 若无自定义配置,则获取默认的容器类型,默认类型为 XmlWebApplicationContext。 * defaultStrategies 读取的配置文件为 ContextLoader.properties, * 该配置文件内容如下: * org.springframework.web.context.WebApplicationContext = * org.springframework.web.context.support.XmlWebApplicationContext */ contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }简单说一下 createWebApplicationContext 方法的流程,该方法首先会调用 determineContextClass 判断创建什么类型的容器,默认为 XmlWebApplicationContext。然后调用 instantiateClass 方法通过反射的方式创建容器实例。instantiateClass 方法就不跟进去分析了,大家可以自己去看看,比较简单。
继续往下分析,接下来分析一下 configureAndRefreshWebApplicationContext 方法的源码。如下:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 从 ServletContext 中获取用户配置的 contextId 属性 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { // 设置容器 id wac.setId(idParam); } else { // 用户未配置 contextId,则设置一个默认的容器 id wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); // 获取 contextConfigLocation 配置 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(); }上面的源码不是很长,逻辑不是很复杂。下面简单总结 configureAndRefreshWebApplicationContext 方法主要做了事情,如下:
设置容器 id
获取 contextConfigLocation 配置,并设置到容器中
刷新容器
到此,关于业务容器的创建过程就分析完了,下面我们继续分析 Web 容器的创建过程。
2.2 Web 容器的创建过程