zoukankan      html  css  js  c++  java
  • ContextLoaderListener加载过程

    在web.xml中,配置ContextLoaderListener

     <!-- 配置Listener,用来创建Spring容器 -->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
      <context-param>
      	<!-- 配置Listener参数:告诉它Spring的配置文件位置,它好去创建容器 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-context*.xml</param-value>
      </context-param>
    

     

    说明:<listener>标签中,ContextLoaderListener用来创建Spring容器;而<context-param>标签,用来指定了spring配置文件的位置,该两个标签共同起作用,也就可以在web项目启动后,成功的创建了Spring容器了。 

     

    分析如下:

    1.ContextLoaderListener实现了ServletContextListener接口,ServletContextListener是Java EE标准接口之一,类似tomcat,jetty的java容器启动时便会触发该接口的contextInitialized。也就是说会激活调用ContextLoaderListener的contextInitialized()方法

    2.ContextLoaderListener类中的contextInitialized方法如下

    	/**
    	 * Initialize the root web application context.
    	 */
    	@Override
    	public void contextInitialized(ServletContextEvent event) {
    		initWebApplicationContext(event.getServletContext());
    	}
    

      

    3.我们在进入到ClassLoader类的initWebApplicationContext( )方法内部,会注入ServletContext   servletContext的参数对象,同时帮我们返回一个WebApplicationContext容器对象,代码如下:

      

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    		
    	/** 此处省略部分代码 */
    
    	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 {
    		
    		if (this.context == null) {
    			/**
    			* 重要,该方法会帮我们创建WebApplicationConntext,并赋值给ContextLoaderListener类的context对象。
    			* 说明:ContextLoaderListener类的context属性来自于它的父类
    			*/
    			this.context = createWebApplicationContext(servletContext);
    		}
    		if (this.context instanceof ConfigurableWebApplicationContext) {
    			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
    			if (!cwac.isActive()) {
    
    				/** 此处省略部分代码 */
    				
    				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;
    		}
    		
    		/** 此处省略部分代码 */
    
    		return this.context;
    	}
    		
    }
    

     

      debug打断点我们可以看到注入ServletContext   servletContext的参数对象ApplicationContextFacade类型的对象,内部是通过Map来实现的,用来加载Spring的配置文件。

      也就是说该方法执行完后,我们就会拿到WebApplicationContext类型的Spring容器对象。那么该方法内部具体是如何加载Spring配置文件的呢,是如何创建WebApplicationContext对象?我们接着往下看

     

      4.我们可以看到该行代码:this.context = createWebApplicationContext(servletContext);  该方法用于创建WebApplicationContext对象,并赋值给context属性。WebApplicationContext是一个接口类,本质上返回的是XmlWebApplicationContext,它就是Spring容器了

     

      5.我们再进入到createWebApplicationContext(servletContext)方法中:

    	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    		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);
    	}
    

      会将刚才注入的ApplicationContextFacade对象传入,用于指定Spring的配置文件位置,它的内部是通过Map来实现的,也就是说,该类会解析<context-param>标签的内容,加载Spring配置文件,有了配置文件就可以初始化容器了

      

      6.createWebApplicationContext( ) 调用determineContextClass( )方法

         determineContextClass有如下代码:

      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
    

     

     7.很明显是从defaultStrategies中加载的

      ContextLoader 类中有段静态代码:会加载默认配置,也就是会加载<param-value>classpath:application-context*.xml</param-value>中的xml文件

        static {
    		try {
    			ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
    			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    		}
    		catch (IOException ex) {
    			throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    		}
    	}
    

      

       8.ContextLoader.properties 文件内容如下:

      org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
    

      此时,determineContextClass( )方法返回的是XmlWebApplicationContext对象,之后回到3步骤中的configureAndRefreshWebApplicationContext( )方法

     

      9.  configureAndRefreshWebApplicationContext的细节调用

        configureAndRefreshWebApplicationContext 调用了AbstractApplicationContext的refresh方法

        refresh 方法调用了obtainFreshBeanFactory

        obtainFreshBeanFactory 调用了AbstractRefreshableApplicationContext类的refreshBeanFactory方法

        refreshBeanFactory调用了XmlWebApplicationContext的loadBeanDefinitions

        loadBeanDefinitions中加载了对应的applicationContext.xml

  • 相关阅读:
    MFC Bitmap::FromBITMAPINFO返回空问题
    String成员函数
    用xshell连接l自己的inux
    回调函数
    文件操作相关函数(POSIX 标准 open,read,write,lseek,close)
    Linux_GDB调试学习笔记
    程序中的一些限制(基于Linux系统C语言)
    第10课:[实战] Redis 网络通信模块源码分析(3)
    第09课:【实战】Redis网络通信模块源码分析(2)
    简单模拟多段线绘制Pline命令过程的撤销功能
  • 原文地址:https://www.cnblogs.com/quan-coder/p/8436471.html
Copyright © 2011-2022 走看看