Spring的启动流程

Tomcat的启动

Tomcat的基本结构

Tomcat容器分为四个等级,Engine--Host--Servlet—Context(一个Context对应一个web工程)
一个 Context 对应一个 Web 工程,所以我们在Tomcat根目录的webapps文件夹路径下面经常会看到除了我们自己部署的web,还有若干其他Tomcat自带的web,不同的web工程都会对应在Tomcat里面的context容器。

Tomcat启动时序图

Tomcat的启动

( Tomcat是采用了一种观察者模式的设计方式,所有的容器都会继承 Lifecycle 接口,它管理容器的整个生命周期,所有容器的的修改和状态的改变都会由它去通知已经注册的观察者(Listener))

1.Tomcat的启动是从顶层开始一直到Engine到Host再到StandardContext(这里的context我可以理解为就是对应每个web工程的容器了)
2.当 Context 容器初始化状态设为 init 时,添加在 Context 容器的 Listener 将会被调用。ContextConfig 继承了 LifecycleListener 接口,ContextConfig 类会负责整个 Web 应用的配置文件的解析工作。

Web应用的启动

3.Tomcat调用ContextConfig的configureStart方法解析web.xml的所有属性(包括contextConfigLocation,ContextLoaderListener),将这些属性保存到WebXml对象中
4.Tomcat会创建一个ServletContext容器,并将WebXml对象的属性设置到servletContext中
5.Context容器会创建web.xml配置的监听器类Listener,并且监听器类必须要实现ServletContextListener接口
public interface ServletContextListener extends EventListener {
    //初始化Spring容器
    void contextInitialized(ServletContextEvent var1);
    //用于关闭应用前释放资源,比如说数据库连接的关闭。
    void contextDestroyed(ServletContextEvent var1);
}
  • web.xml 属性都被解析到 Context 中,所以说 Context 容器才是真正运行 Servlet 的 Servlet 容器。
  • web.xml作用:容器的配置属性由应用的 web.xml 指定

Spring容器的启动

流程图:

6.ContextLoaderListener实现了用来监听ServletContext事件的ServletContextListener 这个接口,如果 ServletContext 状态发生变化,将会触发产生对应的ServletContextEvent,然后调用监听器的不同的方法。

public class ContextLoaderListener extends ContextLoader implementsServletContextListener {
...
  //实现了ServletContextListener的初始化接口
  public void contextInitialized(ServletContextEvent event) {
    this.context = createContextLoader();
    if(this.contextLoader == null){
      this.contextLoader = this;
    }
    this.contextLoader.initWebApplicationContext(event.getServletContext());
  }
}

  

7.Servlet启动之后会调用ServletContextListener的contextInitialized方法

8.contextInitialized回调initWebApplicationContext方法,创建并初始化Spring容器的WebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		//对WebApplicationContext存在性验证
		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!");
		}

		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
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof 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, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//加载Spring配置文件,并创建Beans
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			//将Spring容器context挂载到ServletContext(web容器上下文)中
			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.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");
			}

			return this.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;
		}
	}

  

初始化WebApplicationContext

9.对WebApplicationContext存在性验证: 在配置中只允许声明一次ServletContextListener,多次声明会扰乱Spring的执行逻辑,验证方式为:查看ServletContext实例中是否有对应key的属性,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,如果存在key则抛出异常,如果不存在key,则可以创建WebApplicationContext实例

10.调用createWebApplicationContext方法创建WebApplicationContext: 程序首先会读取ContextLoader类的同目录下的属性文件ContextLoader.properties,并根据其中的配置提取将要实现WebApplicationContext接口的实现类,并根据这个实现类通过反射的方式进行实例的创建

11.调用configureAndRefreshWebApplicationContext方法加载Spring配置文件,并创建beans

12.通过setAttribute方法,将Spring容器context挂载到ServletContext(web容器上下文)中.

 

 

 

参考:
https://blog.csdn.net/u013510838/article/details/75066884
Spring源码深度解析
posted @ 2018-04-30 16:02  KristinLee  阅读(261)  评论(0编辑  收藏  举报
jQuery火箭图标返回顶部代码