zoukankan      html  css  js  c++  java
  • spring启动入口

    spring启动入口

    (44条消息) spring启动入口_lieyanhaipo的专栏-CSDN博客_spring 入口类

    一、Spring与WEB容器整合   

    web项目中,Spring启动是在web.xml配置监听器,如下所示: 

    Xml代码   收藏代码
    1. <!-- 配置Spring上下文监听器 -->  
    2. <listener>  
    3.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    4. </listener>  

     
      可以看看ContextLoaderListener类,它实现了Tomcat容器的ServletContextListener接口,所以它与普通的Servlet监听是一样的。同样是重写到两个方法:contextInitialized()方法在web容器初始化时执行,contextDestroyed()方法在容器销毁时执行。

        WEB容器启动时会触发初始化事件,ContextLoaderListener监听到这个事件,其contextInitialized()方法会被调用,在这个方法中Spring会初始化一个根上下文,即WebApplicationContext。这是一个接口,其实际默认实现类是XmlWebApplicationContext。这个就是Spring IOC的容器,其对应bean定义的配置信息由web.xml中的context-param来指定

    Xml代码   收藏代码
    1. <!-- 配置Spring配置文件路径 -->  
    2. <context-param>  
    3.     <param-name>contextConfigLocation</param-name>  
    4.     <param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml  
    5.         </param-value>  
    6. </context-param>  

     在Spring IOC 容器启动初始化完毕之后,会将其储存到ServletContext中。形式如下:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, WebApplicationContext context);



     
    注:以前我们写Servlet程序的时候,如果在Tomcat容器启动的时候需要绑定一些参数或者做一些全局处理,那么就需要实现ServletContextListener接口,在contextInitialized方法中编写必要的代码。然后在web.xml添加配置listener

     在ContextLoaderListener类中,只是实现了ServletContextListener提供的到两个方法,Spring启动主要的逻辑在父类ContextLoader的方法initWebApplicationContext实现。ContextLoaderListener的作用就是启动web容器时自动装配ApplicationContext的配置信息。更细化一点讲,Spring的启动过程其实就是Spring IOC容器的启动过程。

    Java代码   收藏代码
    1. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {  
    2.   
    3.     /** 
    4.      * Initialize the root web application context. 
    5.      */  
    6.     @Override  
    7.     public void contextInitialized(ServletContextEvent event) {  
    8.         initWebApplicationContext(event.getServletContext());  
    9.     }  
    10.   
    11.     /** 
    12.      * Close the root web application context. 
    13.      */  
    14.     @Override  
    15.     public void contextDestroyed(ServletContextEvent event) {  
    16.         closeWebApplicationContext(event.getServletContext());  
    17.         ContextCleanupListener.cleanupAttributes(event.getServletContext());  
    18.     }  
    19. }  

     二、ContextLoader剖析

      从上一部分ContextLoaderListener类可以得知,ContextLoader实际执行Spring容器的初始化,Spring整个的配置工作都是在ContextLoader完成的,这里参数ServletContextEvent由web容器提供,不做说明。ContextLoaderListener很好理解,所以我们主要看ContextLoader类。用Maven引入Spring的源码,打开ContextLoader类,类注释的第一行就是

    Xml代码   收藏代码
    1. /**  
    2.  * Performs the actual initialization work for the root application context.  
    3.   ......  
    4. **/  

     用google翻译:实际执行根应用上下文的初始化工作。这里的根应用上下文就是上文所写的WebApplicationContext。我们先看看ContextLoader的时序图

     ContextLoader类initWebApplicationContext()方法

    Java代码   收藏代码
    1. /** 
    2.  * Initialize Spring's web application context for the given servlet context, 
    3.  * using the application context provided at construction time, or creating a new one 
    4.  * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 
    5.  * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 
    6.  * @param servletContext current servlet context 
    7.  * @return the new WebApplicationContext 
    8.  * @see #ContextLoader(WebApplicationContext) 
    9.  * @see #CONTEXT_CLASS_PARAM 
    10.  * @see #CONFIG_LOCATION_PARAM 
    11.  */  
    12. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
    13.     //判断ServletContext是否已经存在WebApplication,如果存在则抛出异常  
    14.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
    15.         throw new IllegalStateException(  
    16.                 "Cannot initialize context because there is already a root application context present - " +  
    17.                 "check whether you have multiple ContextLoader* definitions in your web.xml!");  
    18.     }  
    19.   
    20.     Log logger = LogFactory.getLog(ContextLoader.class);  
    21.     servletContext.log("Initializing Spring root WebApplicationContext");  
    22.     if (logger.isInfoEnabled()) {  
    23.         logger.info("Root WebApplicationContext: initialization started");  
    24.     }  
    25.     long startTime = System.currentTimeMillis();  
    26.   
    27.     try {  
    28.         // Store context in local instance variable, to guarantee that  
    29.         // it is available on ServletContext shutdown.  
    30.         if (this.context == null) {  
    31.             //创建WebApplicationContext(下文有说明)  
    32.             this.context = createWebApplicationContext(servletContext);  
    33.         }  
    34.         if (this.context instanceof ConfigurableWebApplicationContext) {  
    35.             ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;  
    36.             if (!cwac.isActive()) {  
    37.                 // The context has not yet been refreshed -> provide services such as  
    38.                 // setting the parent context, setting the application context id, etc  
    39.                 if (cwac.getParent() == null) {  
    40.                     // 得到根上下文的父上下文,然后设置到根上下文 。一般的web项目parent为空  
    41.                     ApplicationContext parent = loadParentContext(servletContext);  
    42.                     cwac.setParent(parent);  
    43.                 }  
    44.                 //从web.xml加载参数,初始化跟上下文WebApplicationContext,创建bean工厂和bean对象。  
    45.                 //这个过程比较麻烦,下一篇文章专门分析  
    46.                 configureAndRefreshWebApplicationContext(cwac, servletContext);  
    47.             }  
    48.         }  
    49.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
    50.   
    51.         ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
    52.         if (ccl == ContextLoader.class.getClassLoader()) {  
    53.             currentContext = this.context;  
    54.         }  
    55.         else if (ccl != null) {  
    56.             currentContextPerThread.put(ccl, this.context);  
    57.         }  
    58.   
    59.         if (logger.isDebugEnabled()) {  
    60.             logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
    61.                     WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
    62.         }  
    63.         if (logger.isInfoEnabled()) {  
    64.             long elapsedTime = System.currentTimeMillis() - startTime;  
    65.             logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
    66.         }  
    67.   
    68.         return this.context;  
    69.     }  
    70.     catch (RuntimeException ex) {  
    71.         logger.error("Context initialization failed", ex);  
    72.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
    73.         throw ex;  
    74.     }  
    75.     catch (Error err) {  
    76.         logger.error("Context initialization failed", err);  
    77.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
    78.         throw err;  
    79.     }  
    80. }  

         

        在这个方法中ServletContext是由web容器监听器(ContextLoaderListener)提供。首先判断servlectContext中是否已经存在根上下文,如果存在,则抛出异常;否则通过createWebApplicationContext方法创建新的根上下文。然后通过loadParentContext()方法为其设置父上下文。再通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。 最后把上下文存入servletContext,并且存入currentContextPerThread。至此初始化过程完毕,接下来可以获取WebApplicationContext,进而用getBean("bean name")得到bean。

    createWebApplicationContext()方法用来创建根上下文:

    Java代码   收藏代码
    1. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {  
    2.     //从web.xml配置的contextClass参数中获取上下文类名,如果contextClass为空,则使用默认的。  
    3.         //下文有说明  
    4.     Class<?> contextClass = determineContextClass(sc);  
    5.         //根上下文必须是ConfigurableWebApplicationContext的子类,否则抛出异常  
    6.     if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
    7.         throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
    8.                 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
    9.     }  
    10.     //BeanUtils.instantiateClass工具方法,根据类名创建类  
    11.     return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
    12. }  
     

    determineContextClass()方法返回根上下文的类名

    Java代码   收藏代码
    1. protected Class<?> determineContextClass(ServletContext servletContext) {  
    2.     //从web.xml获得参数contextClass,在一般的web项目中,此参数为null  
    3.     String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);  
    4.     if (contextClassName != null) {  
    5.         try {  
    6.             return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());  
    7.         }  
    8.         catch (ClassNotFoundException ex) {  
    9.             throw new ApplicationContextException(  
    10.                     "Failed to load custom context class [" + contextClassName + "]", ex);  
    11.         }  
    12.     }  
    13.     else {  
    14.         //获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,  
    15.         //在CotnextLoader类开头static语句块中初始化  
    16.         contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());  
    17.         try {  
    18.             return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());  
    19.         }  
    20.         catch (ClassNotFoundException ex) {  
    21.             throw new ApplicationContextException(  
    22.                     "Failed to load default context class [" + contextClassName + "]", ex);  
    23.         }  
    24.     }  
    25. }  
     

    WebApplicationContext默认实现类的类名获取

    Java代码   收藏代码
    1. static {  
    2.     try {  
    3.         //获取当前包下面的ContextLoader.properties文件,文件内容是:  
    4.         //org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext  
    5.         ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);  
    6.         defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
    7.     }  
    8.     catch (IOException ex) {  
    9.         throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());  
    10.     }  
    11. }  
  • 相关阅读:
    在HttpHandlers (ASHX files)中使用Session
    EventCalendar控件源码和ASP.NET 2.0 Beta 2 Starter Kits中可能遇到的问题
    C# 获取数据库中某个某个表的创建脚本[原创]
    Linq 合并多个查询条件
    自定义WCF RIA Services 超时时间
    也来学学插件式开发续利用MEF
    HTML5程序设计 Geolocation API
    反射实体模型生成Oracle SQL脚本
    Entity Framework With Oracle
    Entity Framework Code First在Oracle下的伪实现
  • 原文地址:https://www.cnblogs.com/zkwarrior/p/15549373.html
Copyright © 2011-2022 走看看