生成一个容器 run_spring


web.xml 加载顺序为: context-param(
源码如下:
package javax.servlet;
import java.util.EventListener;
public interface ServletContextListener extends EventListener {
 void contextInitialized(ServletContextEvent var1);//在该方法中底层处理请求的组件,如Bean,数据库连接池,数据库事务管理器配置好等
 void contextDestroyed(ServletContextEvent var1);
) 
< listener (ContextLoaderListener是实现ServletContextListener的实例,获取spring的整体配置信息,并创建WebApplicationContext来存储bean的信息,以及创建这些bean的实例,默认去WEB-INF下加载ApplicationContext.xml,或 使用修改路径名称 <context-param><param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/applicationContext.xml</param-value>
 </context-param>
其中param的key必须为contextConfigLocation,IOC初始化过程就是<context-param>加载的过程,
ContextLoaderListener的源码:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
 public ContextLoaderListener() {
 }
 public ContextLoaderListener(WebApplicationContext context) {
 super(context);
 }
 //初始化ServletContext上下文配置
 public void contextInitialized(ServletContextEvent event) {
 this.initWebApplicationContext(event.getServletContext());
 }
 //销毁ServletContext上下文
 public void contextDestroyed(ServletContextEvent event) {
 this.closeWebApplicationContext(event.getServletContext());
 ContextCleanupListener.cleanupAttributes(event.getServletContext());
 }
}
Spring只有一个容器,具体实现为:LinkedHashMap中set值:
(web容器的上下文,不是Spring的)servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
源代码判断:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
 //1、判断是否已经初始化过了ServletContext
 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
 throw new IllegalStateException(
 "Cannot initialize context because there is already a root application context present - " +
 "check whether you have multiple ContextLoader* definitions in your web.xml!");
 }
 //2、日志记录,显示初始化Spring WebApplicationContext
 servletContext.log("Initializing Spring root WebApplicationContext");
 Log logger = LogFactory.getLog(ContextLoader.class);
 if (logger.isInfoEnabled()) {
 http://logger.info("Root WebApplicationContext: initialization started");
 }
 //3、记录启动耗时/ms
 long startTime = System.currentTimeMillis();
 try {
 // 将上下文存储在本地实例变量中,以确保它在ServletContext关闭时可用
 if (this.context == null) {
 //(1)
 this.context = createWebApplicationContext(servletContext);//此方法初始化上下文,在点此方法,是用反射创建了上下文容器
 protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
 Class<?> contextClass = determineContextClass(sc);//又调用了此方法,又Class<?>可看出是使用反射,OK我们在点 determineContextClass方法看看底层的实现查看(3)
 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);
(3)先解释下
determineContextClass,字面意思为检测Context的class类型,会读取servletContext的初始化参数contextClass,大部分情况下我们不会配置此参数,在未配置的情况下,Spring会去org.springframework.web.context包中的ContextLoader.properties配置文件读取默认配置XmlWebApplicationContext :
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
此参数是关键,它将该类的属性XmlWebApplicationContext的值付给了WebApplicationContext
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
源代码方法实现:protected Class<?> determineContextClass(ServletContext servletContext) {
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 {
 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);
 }
 }
 }
 }
 //(2)
 if (this.context instanceof ConfigurableWebApplicationContext) {
 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
 if (!cwac.isActive()) {
 // 上下文还没有刷新——>提供了诸如设置父上下文、设置应用程序上下文id等服务
 if (cwac.getParent() == null) {
 // 上下文实例是在没有显式父类的情况下注入的——如果有根web应用程序上下文,则确定它为父类
 ApplicationContext parent = loadParentContext(servletContext);
 cwac.setParent(parent);
 }
 //(3)
 configureAndRefreshWebApplicationContext(cwac, servletContext);
 }
 }
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
 ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 if (ccl == ContextLoader.class.getClassLoader()) {
 currentContext = this.context;
 }
 else if (ccl != null) {
 currentContextPerThread.put(ccl, this.context);
 }
 if (logger.isInfoEnabled()) {
 long elapsedTime = System.currentTimeMillis() - startTime;
 http://logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
 }
 return this.context;
 }
 catch (RuntimeException | Error ex) {
 logger.error("Context initialization failed", ex);
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
 throw ex;
 }
• @Configuration:相当于Spring的xml配置文件中的<beans/>标签,即Spring容器的上下文。    
• @Bean:相当于Spring的xml配置文件中的<bean/>标签,即注册一个bean对象。
• 如果没有自定义配置contextClass的话,配置默认的 <param-name>contextConfigLocation</param-name> ,将会默认加载WebApplicationContext实现类 org.springframework.web.context.support.XmlWebApplicationContext。
• 
• 总结:
整个初始化Spring应用上下文的方法 initWebApplicationContext(),主要以下3点:
创建WebApplicationContext。
加载对应的spring配置文件中的Bean。
将WebApplicationContext放入ServletContext(Java Web的全局变量)中。
)
< filter
 < servlet(加载完ContextLoaderListener之后,在加载servlet,ContextLoaderListener中初始化了Bean和Dao,Service,在配置中将Controllerguolv掉了,在mvc.xml中将Controller加载进来,为什么要这么做呢?方便dispatcherServlet进行控制和查找。上面的 contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。)
web.xml 加载顺序结束
Spring的启动是建立在servlet/web容器(Tomcat、JBoss、Jetty等)之上的,一个常规的Spring应用,在web容器启动时,默认会先去加载/WEB-INF/web.xml,它配置了:servleContext上下文、监听器(Listener)、过滤器(Filter)、Servlet等。
常规的web.xml示例:
<!--spring资源上下文定义,在指定路径加载pring的xml配置文件-->
 <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/applicationContext.xml</param-value>
 </context-param>
<!--spring的上下文监听器-->
 <listener>
 <listener-class>
 org.springframework.web.context.ContextLoaderListener
 </listener-class>
 </listener>
<!--编码过滤器,防止请求中文乱码-->
<filter>
 <filter-name>CharacterEncodingFilter</filter-name>
 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
 <init-param>
 <param-name>encoding</param-name>
 <param-value>utf-8</param-value>
 </init-param>
</filter>
<filter-mapping>
 <filter-name>CharacterEncodingFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Springmvc的核心控制器,DispatcherServlet请求分发器-->
 <servlet>
 <servlet-name>dispatchServlet</servlet-name>
 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <init-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath:spring/springmvc.xml</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>dispatchServlet</servlet-name>
 <url-pattern>*.do</url-pattern>
 </servlet-mapping>