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();
    	}
    

    创建流程图如下:

  • 相关阅读:
    Bubble Sort (5775)
    Dertouzos (5750)
    codeforces 360 E
    codeforces 360 D
    codeforces 360 C
    Mike and Cellphone
    训练2——B
    训练1——A
    符号三角形
    Sudoku Killer
  • 原文地址:https://www.cnblogs.com/insaneXs/p/11115862.html
Copyright © 2011-2022 走看看