SpringMVC 的启动流程(源码)

版权声明:未经允许转载,将保留追究其法律责任 https://blog.csdn.net/qq_37025563/article/details/85626435
  • 说明: 我们在以往的web项目里,在启动容器的话都要在web.xml配置文件中配置一个监听器

例:

	<!-- 指定以Listener方式启动Spring容器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
  • 这个web监听器就是容器的初始化的入口,首先看一下这个监听器的继承结构
    在这里插入图片描述

  • ContextLoader类负责执行根应用程序上下文的实际初始化工作,ServletContextListener负责监听web容器的初始化与销毁

  • 整个Spring mvc 容器的启动就是在contextInitialized()方法开始的,当整个web容器启动时,web容器就会调用该方法,这也是典型的观察者模式的应用

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	
	public ContextLoaderListener() {	}

	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	
   @Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}
  • 接下来跟踪initWebApplicationContext(event.getServletContext())};
    event.getServletContext()可以拿到seevlet的上下文对象,当做参数传入方法中,那么接下来我们看看核心方法中的代码
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		// 获取上下文域中属性,以String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"为key去取;如果已经存在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.
			// 如果WebApplicationContext为空则创建一个WebApplicationContext容器,方法里面有默认的选择容器的策略
			// 默认容器存放在ContextLoader.properties文件中为XmlWebApplicationContext
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			// XmlWebApplicationContext 是ConfigurableWebApplicationContext的实现类
			if (this.context instanceof ConfigurableWebApplicationContext) {
			   // 向上转型为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);
					}
					// 刷新web容器
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			// 刷新容器以后,放入servletContext域中
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
			// 判断当前线程所携带的类加载器,与加载ContextLoader类的类加载器是否一致,如果一致则存放到
			// ClassLoader 的静态的当前容器变量中
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			// 如果不一致,则存放在private static final Map<ClassLoader, WebApplicationContext>   currentContextPerThread =
			new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);中
			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;
		}
	}
  • 目前我们对spring mvc 整个容器的初始化有了一个初步的概念,如果需要知道更多细节,比如怎么取加载我们的application.xml文件中的标签实现初始化,还请自行去研究
  • 初次写个人博客,如有不正还请多多指出

猜你喜欢

转载自blog.csdn.net/qq_37025563/article/details/85626435