zoukankan      html  css  js  c++  java
  • spring源码研究之IoC容器在web容器中初始化过程

    转载自 http://ljbal.iteye.com/blog/497314

    前段时间在公司做了一个项目,项目用了spring框架实现,WEB容器是Tomct 5,虽然说把项目做完了,但是一直对spring的IoC容器在web容器如何启动和起作用的并不清楚。所以就抽时间看一下spring的源代码,借此了解它的原理。

        我们知道,对于使用Spring的web应用,无须手动创建Spring容器,而是通过配置文件,声明式的创建Spring容器。因此在Web应用中创建Spring容器有如下两种方式:

        1. 直接在web.xml文件中配置创建Spring容器。

        2. 利用第三方MVC框架的扩展点,创建Spring容器。

        其实第一种方式是更加常见。为了让Spring容器随Web应用的启动而启动,有如下两种方式:

        1. 利用ServletContextListener实现。

        2. 利用load-on-startup Servlet实现。

        Spring提供ServletContextListener的一个实现类ContextLoaderListener,该类可以作为Listener 使用,它会在创建时自动查找WEB-INF下的applicationContext.xml文件,因此,如果只有一个配置文件,并且文件名为applicationContext.xml,则只需在web.xml文件中增加以下配置片段就可以了。

       

    Java代码  收藏代码
    1. <listener>  
    2.       <listener-class>  
    3.      org.springframework.web.context.ContextLoaderListener  
    4.       </listener-class>  
    5. </listener>  

        如果有多个配置文件需要载入,则考虑使用<context-param...>元素来确定配置文件的文件名。ContextLoaderListener加载时,会查找名为contentConfigLocation的初始化参数。因此,配置<context-param...>时就指定参数名为contextConfigLocation。

        带多个配置文件的web.xml文件如下:

       

    S代码  收藏代码
    1. <context-param>    
    2.           <param-name>contextLoaderListener</param-name>  
    3.      <param-value>     
    4.                    WEB-INF/*.xml, classpath:spring/*.xml  
    5.                  </param-value>  
    6. </context-param>  
    S代码  收藏代码
    1. <listener>  
    2.       <listener-class>  
    3.      org.springframework.web.context.ContextLoaderListener  
    4.       </listener-class>  
    5. </listener>  

        多个配置文件之间用“,”隔开。

        下面我们来看它的具体实现过程是怎样的,首先我们从ContextLoaderListener入手,它的代码如下:

       

    Java代码  收藏代码
    1. public class ContextLoaderListener implements ServletContextListener   
    2. {  
    3.   
    4.     private ContextLoader contextLoader;  
    5.   
    6.   
    7.     /** 
    8.      * 这个方法就是用来初始化web application context的 
    9.      */  
    10.     public void contextInitialized(ServletContextEvent event)   
    11.                 {  
    12.         this.contextLoader = createContextLoader();  
    13.         this.contextLoader.initWebApplicationContext(event.getServletContext());  
    14.     }  
    15.   
    16.     /** 
    17.      * 创建一个contextLoader. 
    18.      * @return the new ContextLoader 
    19.      */  
    20.     protected ContextLoader createContextLoader()  
    21.                 {  
    22.         return new ContextLoader();  
    23.     }  
    24.      ................  
    25.              
    26. }  

       我们看到初始化web application context的时候,首先通过new ContextLoader()创建一个contextLoader,

       new ContextLoader()具体做了什么事呢?ContextLoader的代码片段:

       

    Java代码  收藏代码
    1. static {  
    2.     try {  
    3.         // 这里创建一个ClassPathResource对象,载入ContextLoader.properties,用于创建对应的ApplicationContext容器  
    4.         // 这个文件跟ContextLoader类在同一个目录下,文件内容如:  
    5.         // org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext  
    6.         // 如此说来,spring默认初始化的是XmlWebApplicationContext  
    7.         ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);  
    8.         // 得到一个Properties对象,后面根据类名来创建ApplicationContext容器  
    9.         defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
    10.            }  
    11.            catch (IOException ex) {  
    12.          throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());  
    13.            }  
    14.             }  

        代码注释里面已经说得很清楚了,很容易理解吧?嘿嘿......

        再下来我们再看一下initWebApplicationContext方法的实现过程:

       

    Java代码  收藏代码
    1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  
    2.             throws IllegalStateException, BeansException {  
    3.   
    4.         // 从servletContext中获取ApplicationContext容器;如果已经存在,则提示初始化容器失败,检查web.xml文件中是否定义有多个容器加载器  
    5.         // ServletContext接口的简述:public interface ServletContext  
    6.         // 定义了一系列方法用于与相应的servlet容器通信,比如:获得文件的MIME类型,分派请求,或者是向日志文件写日志等。  
    7.         // 每一个web-app只能有一个ServletContext,web-app可以是一个放置有web application 文件的文件夹,也可以是一个.war的文件。  
    8.         // ServletContext对象包含在ServletConfig对象之中,ServletConfig对象在servlet初始化时提供servlet对象。  
    9.   
    10.         if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
    11.             throw new IllegalStateException(  
    12.                     "Cannot initialize context because there is already a root application context present - " +  
    13.                     "check whether you have multiple ContextLoader* definitions in your web.xml!");  
    14.         }  
    15.   
    16.         servletContext.log("Initializing Spring root WebApplicationContext");  
    17.         if (logger.isInfoEnabled()) {  
    18.             logger.info("Root WebApplicationContext: initialization started");  
    19.         }  
    20.         long startTime = System.currentTimeMillis();  
    21.   
    22.         try {  
    23.             // Determine parent for root web application context, if any.  
    24.             // 获取父容器  
    25.             ApplicationContext parent = loadParentContext(servletContext);  
    26.   
    27.             // Store context in local instance variable, to guarantee that  
    28.             // it is available on ServletContext shutdown.  
    29.             // 创建ApplicationContext容器  
    30.             this.context = createWebApplicationContext(servletContext, parent);  
    31.             // 把容器放入到servletContext中  
    32.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
    33.   
    34.             if (logger.isDebugEnabled()) {  
    35.                 logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
    36.                         WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
    37.             }  
    38.             if (logger.isInfoEnabled()) {  
    39.                 long elapsedTime = System.currentTimeMillis() - startTime;  
    40.                 logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
    41.             }  
    42.   
    43.             return this.context;  
    44.         }  
    45.         catch (RuntimeException ex) {  
    46.             logger.error("Context initialization failed", ex);  
    47.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
    48.             throw ex;  
    49.         }  
    50.         catch (Error err) {  
    51.             logger.error("Context initialization failed", err);  
    52.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
    53.             throw err;  
    54.         }  
    55.     }  

        从上面的代码可以看出,我们创建好的applicationContext容器会放在servletContext中。servletContext是什么  呢?

        在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象。在这个上下文的基础上,和web MVC相关还会有一个上下文来保存控制器之类的MVC对象,这样就构成了一个层次化的上下文结构。

        从initWebApplicationContext中可以看到真正创建applicationContext容器是由createWebApplicationContext方法来实现的,它的代码如下:

       

    Java代码  收藏代码
    1. protected WebApplicationContext createWebApplicationContext(  
    2.             ServletContext servletContext, ApplicationContext parent) throws BeansException   
    3.     {  
    4.         // 首先决定要创建的applicationContext容器的类  
    5.         Class contextClass = determineContextClass(servletContext);  
    6.         // 如果获取到的类不是ConfigurableWebApplicationContext类型的,则创建容器失败,所以这里创建的容器必须是ConfigurableWebApplicationContext类型的  
    7.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))   
    8.         {  
    9.             throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
    10.                     "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
    11.         }  
    12.   
    13.         // 实例化spring容器  
    14.         ConfigurableWebApplicationContext wac =  
    15.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
    16.         wac.setParent(parent);  
    17.         wac.setServletContext(servletContext);  
    18.         // 获取contextConfigLocation初始化参数,该参数记录的是需要载入的多个配置文件(即定义bean的配置文件)  
    19.         String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);  
    20.         if (configLocation != null)   
    21.         {  
    22.             wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,  
    23.                     ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  
    24.         }  
    25.   
    26.         wac.refresh();  
    27.         return wac;  
    28.     }  

        createWebApplicationContext方法实现步骤为:

        1. 首先决定要创建的applicationContext容器的类
        2. 实例化applicationContext容器

        但它是如何决定要创建的容器类呢?我们看一下determineContextClass方法:

       

    Java代码  收藏代码
    1. protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException   
    2.     {  
    3.         // 从web.xml中获取需要初始化的容器的类名  
    4.         String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);  
    5.         // 如果获取到的类名不为空,则创建该容器的Class对象  
    6.         if (contextClassName != null)   
    7.         {  
    8.             try {  
    9.                 return ClassUtils.forName(contextClassName);  
    10.             }  
    11.             catch (ClassNotFoundException ex) {  
    12.                 throw new ApplicationContextException(  
    13.                         "Failed to load custom context class [" + contextClassName + "]", ex);  
    14.             }  
    15.         }  
    16.         // 否则创建默认的容器的Class对象,即:org.springframework.web.context.support.XmlWebApplicationContext  
    17.         // 在创建ContextLoader时,defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);这句代码已经准备好默认的容器类  
    18.         else   
    19.         {  
    20.             contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());  
    21.             try   
    22.             {  
    23.                 return ClassUtils.forName(contextClassName);  
    24.             }  
    25.             catch (ClassNotFoundException ex)   
    26.             {  
    27.                 throw new ApplicationContextException(  
    28.                         "Failed to load default context class [" + contextClassName + "]", ex);  
    29.             }  
    30.         }  
    31.     }  

        该方法首先判断从web.xml文件的初始化参数CONTEXT_CLASS_PARAM(的定义为public static final String CONTEXT_CLASS_PARAM = "contextClass";)获取的的类名是否存在,如果存在,则容器的Class;否则返回默认的

    Class。如何获取默认的容器Class,注意看创建contextLoader时的代码注释就知道了。

        由此看来,spring不仅有默认的applicationContext的容器类,还允许我们自定义applicationContext容器类,不过Spring不建义我们自定义applicationContext容器类。

       

       好了,这就是spring的IoC容器在web容器如何启动和起作用的全部过程。细心的朋友可以看出创建applicationContext容器的同时会初始化配置文件中定义的bean类,createWebApplicationContext方法中的wac.refresh();这段代码就是用来初始化配置文件中定义的bean类的。它具体的实现过程现在还没完全搞清楚,等搞清楚了再跟大家分享!

  • 相关阅读:
    MongoDB 介绍和操作
    公钥私钥的那点事儿
    这是你了解的 print()函数吗
    Windows应急响应和系统加固(7)——Windows操作系统日志分析
    Windows应急响应和系统加固(6)——Windows历年高危漏洞介绍和分析
    Windows应急响应和系统加固(4)——Windows帐号角色权限的安全检查分析以及PowerShell的使用介绍
    Windows应急响应和系统加固(3)——Windows操作系统的帐号角色权限
    Windows应急响应和系统加固(2)——Windows应急响应的命令使用和安全检查分析
    Windwos应急响应和系统加固(1)——Windwos操作系统版本介绍
    Pycharm中配置鼠标悬停快速提示方法参数
  • 原文地址:https://www.cnblogs.com/chenying99/p/3273671.html
Copyright © 2011-2022 走看看