Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 286|回复: 0

[默认分类] Spring源码系列:Spring的启动过程

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-4-26 10:46:26 | 显示全部楼层 |阅读模式


      Spring对于程序员说来说都不陌生;作为一个强大的开源技术,帮助我们能够更好的进行项目的开发与维护。直接进入主题吧。Spring的启动过程实际上就是Ioc容器初始化以及载入Bean的过程;本文主要是学习记录下前半部分(Ioc容器的初始化),新手上路,如有错误,请指正!1.从配置文件说起
    1. [code]<listener>  
    2.      <listener-class>org.springframework.web.context.ContextLoaderListener
    3.      </listener-class>  
    4. </listener>
    5. <context-param>  
    6.     <param-name>contextConfigLocation</param-name>  
    7.     <param-value>classpath:applicationContext.xml</param-value>  
    8. </context-param>
    复制代码
    [/code]
      在一般的WEB项目中,项目的启动一般是从web.xml文件的载入开始的。如果我们的项目中使用了Spring,那么你肯定会在你的web.xml文件中看到上面的配置。Spring正是通过ContextLoaderListener监听器来进行容器初始化的。下面通过代码进行分析。
      2.Spring容器加载的三步走
      
       step1:创建一个WebApplicationContext
       step2:配置并且刷新Bean
       step3:将容器初始化到Servlet上下文中
      
      3.WebApplicationContext的创建过程
    1. [code]public class ContextLoaderListener extends ContextLoader implements ServletContextListener
    复制代码
    [/code]
      从ContextLoaderListener的定义可以看出,该监听器继承了ContextLoader,并且重写了ServletContextListener中的contextInitialized和contextDestroyed方法。
      在contextInitialized中,通过调用父类(ContextLoader)的initWebApplicationContext方法进行容器创建:
    1. [code]@Override
    2. public void contextInitialized(ServletContextEvent event) {
    3.     initWebApplicationContext(event.getServletContext());
    4. }
    复制代码
    [/code]
      下面来看initWebApplicationContext的代码:
    1. [code]public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    2.     //1:判断当前容器是否存在,如果存在则报容器已经存在的异常信息
    3.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
    4.       throw new IllegalStateException(
    5.           "Cannot initialize context because there is already a root application context present - " +
    6.           "check whether you have multiple ContextLoader* definitions in your web.xml!");
    7.     }
    8.     Log logger = LogFactory.getLog(ContextLoader.class);
    9.     //下面这个日志就是我们经常在启动Spring项目时看到的日志信息:
    10.     //Initializing Spring root WebApplicationContext
    11.     //Root WebApplicationContext: initialization started
    12.     servletContext.log("Initializing Spring root WebApplicationContext");
    13.     if (logger.isInfoEnabled()) {
    14.       logger.info("Root WebApplicationContext: initialization started");
    15.     }
    16.     long startTime = System.currentTimeMillis();
    17.     try {
    18.       // Store context in local instance variable, to guarantee that
    19.       // it is available on ServletContext shutdown.
    20.       //如果当前容器为null,则创建一个容器,并将servletContext上下文作为参数传递进去,
    21.       if (this.context == null) {
    22.         this.context = createWebApplicationContext(servletContext);
    23.       }
    24.        //判断当前容器是否为可配置的,只有是Configurable的容器,才能进行后续的配置
    25.       if (this.context instanceof ConfigurableWebApplicationContext) {
    26.         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
    27.         if (!cwac.isActive()) {
    28.           // The context has not yet been refreshed -> provide services such as
    29.           // setting the parent context, setting the application context id, etc
    30.           if (cwac.getParent() == null) {
    31.             // The context instance was injected without an explicit parent ->
    32.             // determine parent for root web application context, if any.
    33.             ApplicationContext parent = loadParentContext(servletContext);
    34.             cwac.setParent(parent);
    35.           }
    36.            //三步走中的第二步:配置并且刷新当前容器
    37.           configureAndRefreshWebApplicationContext(cwac, servletContext);
    38.         }
    39.       }
    40.        //将配置并且刷新过的容器存入servlet上下文中,并以WebApplicationContext的类名作为key值
    41.       servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    42.       ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    43.       if (ccl == ContextLoader.class.getClassLoader()) {
    44.         currentContext = this.context;
    45.       }
    46.       else if (ccl != null) {
    47.         currentContextPerThread.put(ccl, this.context);
    48.       }
    49.       if (logger.isDebugEnabled()) {
    50.         logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
    51.             WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
    52.       }
    53.       if (logger.isInfoEnabled()) {
    54.         long elapsedTime = System.currentTimeMillis() - startTime;
    55.         logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
    56.       }
    57.        //返回创建好的容器
    58.       return this.context;
    59.     }
    60.     catch (RuntimeException ex) {
    61.       logger.error("Context initialization failed", ex);
    62.       servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
    63.       throw ex;
    64.     }
    65.     catch (Error err) {
    66.       logger.error("Context initialization failed", err);
    67.       servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
    68.       throw err;
    69.     }
    70.   }
    复制代码
    [/code]
      下面我们在看下是如何创建WebApplicationContext的
    1. [code]protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    2.     //首先来确定context是由什么类定义的,并且判断当前容器是否为可配置的
    3.     Class<?> contextClass = determineContextClass(sc);
    4.     if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    5.       throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
    6.           "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    7.     }
    8.     //创建可配置的上下文容器
    9.     return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    10.   }
    复制代码
    [/code]
      最后来看下determineContextClass这个方法
    1. [code]protected Class<?> determineContextClass(ServletContext servletContext) {
    2.     //首先从web.xml中查看用户是否自己定义了context
    3.     String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    4.     //如果有,则通过反射创建实例
    5.     if (contextClassName != null) {
    6.       try {
    7.         return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
    8.       }
    9.       catch (ClassNotFoundException ex) {
    10.         throw new ApplicationContextException(
    11.             "Failed to load custom context class [" + contextClassName + "]", ex);
    12.       }
    13.     }
    14.     /*如果没有,则去defaultStrategies里面取【defaultStrategies是Propertites类的/对象,在ContextLoader中的静态代码块中初始化的;具体可看下下面的图像】;默认容器是XmlWebApplicationContext*/
    15.   else {
    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.   }
    复制代码
    [/code]
      总的来说就是:Spring的web工程首先回去检查用户是否自己定义了context,如果有就采用;如果没有就使用Spring默认的。defaultStrategies初始化:
      
      
      至此,容器创建完成。下面是整个过程的一个流程图(有疏漏,回头补一个时序图):
      
      





    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-16 22:10 , Processed in 0.367190 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表