zoukankan      html  css  js  c++  java
  • SpringMVC Root WebApplicationContext启动流程

    传统的SpringMVC项目中,需要在web.xml中配置ContextlistenerContextLoaderListener是负责引导启动和关闭Spring的Root上下文的监听器。主要将处理委托给ContextLoaderContextCleanupListener
    类的继承关系。
    image
    ContextLoaderListener实现了ServletContextListener接口。该接口主要定义了两个行为:监听上下文创建(contextInitialized)和监听上下文销毁(contextDestroyed)。
    ContextLoaderListener只是将方法的处理委托给ContextLoader

    package org.springframework.web.context;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    
    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    
    	public ContextLoaderListener() {
    	}
    
    	
    	public ContextLoaderListener(WebApplicationContext context) {
    		super(context);
    	}
    
    
    	//初始化Root WebApplication
    	@Override
    	public void contextInitialized(ServletContextEvent event) {
    	//委托给ContextLoader
    		initWebApplicationContext(event.getServletContext());
    	}
    
    
    	//销毁Root WebApplication
    	@Override
    	public void contextDestroyed(ServletContextEvent event) {   
    	//委托给ContextLoader
    		closeWebApplicationContext(event.getServletContext());
    		//委托给ContextCleanupListener
    		ContextCleanupListener.cleanupAttributes(event.getServletContext());
    	}
    
    }
    
    

    ContextLoaderinitWebApplicationContext方法负责创建root web application context。

    //初始化root web application context
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    		//校验是否已经存在root application,
    		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,并保存到变量中,等关闭时使用
    			if (this.context == null) {
    				this.context = createWebApplicationContext(servletContext);
    			}
    			//如果是ConfigurableWebApplicationContext,则配置上下文并刷新
    			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);
    					}
    					configureAndRefreshWebApplicationContext(cwac, servletContext);
    				}
    			}
    			//在ServletContext中设置属性,表明已经配置了root web application context
    			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;
    		}
    	}
    

    该方法主要分为四步:

    1. 校验是否已经存在root WebApplicationContext
    2. 创建root WebApplicationContext
    3. 配置上下文并刷新
    4. 绑定root WebApplicationContext到servlet context上

    这四步具体分析如下:

    1.校验

    校验的过程比较简单,主要是判断Servlet Context是否已经绑定过WebApplicationContext

    2.创建对象

    createWebApplicationContext()方法负责创建WebApplicationContext对象,主要过程是确定WebApplicationContext类,并实例化:

    	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    	//查找WebApplicationContext的类
    		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);
    	}
    

    决定WebApplicationContext类的主要策略是先判断ServletContext是否配置了contextClass属性,如果是,则用该属性指定的类作为WebApplicationContext类,否则则是否默认策略。默认策略是根绝classpath下的ContextLoader.properties中配置的类作为WebApplicatioNCOntext类:

    protected Class<?> determineContextClass(ServletContext servletContext) {
    		//优先根据servletContext配置的contextClass属性
    		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);
    			}
    		}
    		//在通过classpath下的`ContextLoader.properties`文件制定的类作为WebApplicationContext类
    		//默认是XmlWebApplicationContext
    		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);
    			}
    		}
    	}
    
    配置与刷新

    创建WebApplicationContext对象后,ContextLoader会判断是否是ConfigurableWebApplicationContext
    ConfigurableWebApplicationContext拓展了WebApplicationContext的能力。其不仅扩展了自身方法,还继承了ConfigurableApplicationContext接口。
    image

    配置与刷新的步骤也正是利用了这些接口提供的能力。

    	//配置并刷新上下文
    	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    		//设置ID
    		if (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()));
    			}
    		}
    
    		//WebApplicationContext绑定Servlet Context
    		wac.setServletContext(sc);
    		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 instanceof ConfigurableWebEnvironment) {
    			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    		}
    
    		//自定义上下文初始过程,留给用户的拓展机制
    		customizeContext(sc, wac);
    		//刷新上下文,加载bean
    		wac.refresh();
    	}
    

    创建流程图如下:

  • 相关阅读:
    swift 第十四课 可视化view: @IBDesignable 、@IBInspectable
    swift 第十三课 GCD 的介绍和使用
    swift 第十二课 as 的使用方法
    swift 第十一课 结构体定义model类
    swift 第十课 cocopod 网络请求 Alamofire
    swift 第九课 用tableview 做一个下拉菜单Menu
    swift 第八课 CollectView的 添加 footerView 、headerView
    swift 第七课 xib 约束的优先级
    swift 第六课 scrollview xib 的使用
    swift 第五课 定义model类 和 导航栏隐藏返回标题
  • 原文地址:https://www.cnblogs.com/insaneXs/p/11115862.html
Copyright © 2011-2022 走看看