一、概述

从事Java开发的朋友一定都知道Spring框架。为什么Spring框架能受到如此多开发者的青睐呢?这得益于Spring提供的诸多便捷实用的功能如:IOC、AOP,并且其可扩展性使得集成第三方开源框架和类库变得异常简单。我们可以访问一下Spring官网【http://spring.io】通过官网的描述去详细了解它,这里就不再过多赘述。本篇就让我们来通过Spring的源码来探究下Spring的启动原理。


二、构建Spring项目

学习一个框架的基本流程首先是学会使用它,然后在使用中一步一步的探究它的核心思想以及底层原理。

首先我们来搭建一个简单的Spring项目,然后根据这个项目来探究其底层实现逻辑,如下图便是我用来探究Spring源码的项目。(代码存放在码云,连接:https://gitee.com/GPF1217/java-from-xiaobai-to-architect.git,读者朋友可自行下载)

springcolud源码解读与原理分析 spring源码讲解_xml

项目已经构建完毕,现在我们先看下webapp下的web.xml文件,里面代码如下

<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  
  <!-- 配置参数 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application.xml</param-value>
  </context-param>
  <!-- 监听启动 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

我们关注的点有两个部分,第一个是<context-param>标签,第二个是<listener>标签,Tomcat容器在启动的时候会扫描这个web.xml文件,读取文件的内容,进行相应的处理。例如:读取到<listener>标签后便会加载<listener-class>标签配置的内容,我们便以这个为入口,去源码里面一探究竟……

springcolud源码解读与原理分析 spring源码讲解_初始化_02

进入ContextLoaderListener类中,首先类上的注释:“Bootstrap listener to start up and shut down Spring's root ”翻译过来就是:“监听Spring根启动和关闭的引导器”,从这段注释中我们便可知道这个类的作用是什么了。用类比的话来说:这就是Spring的“大门”,探究Spring要从这里进入。

我们首先看下ContextLoaderListener这个类里面的方法,我们可以发现除了构造器以外,它有两个方法,分别是contextInitialized和contextDestroyed,从两个方法的名字中我们就可以看出,一个是启动方法另一个是销毁方法,如下图

springcolud源码解读与原理分析 spring源码讲解_spring_03

接下来我们通过debug模式看下它的启动流程具体是什么……我们将断点打在103行,initWebApplicationContext处,然后启动Tomcat容器……启动后我们会发现程序进入断点,然后我们进入这个方法,定位到第292行configureAndRefreshWebApplicationContext,从方法命中就可以看出,该方法的作用是:配置和刷新Web应用环境。如下图

springcolud源码解读与原理分析 spring源码讲解_xml_04

我们进入这个方法继续看(因为Spring的内容很多,我们只关注核心点)

springcolud源码解读与原理分析 spring源码讲解_spring_05

在387行有一个getInitParameter方法,里面的参数是contextConfigLocation,这个参数是不是很熟悉?没错就是我们一开始在web.xml里<context-param>配置的属性,该方法获取了我们在web.xml里面配置的classpath:application.xml这个值,接着我们再定位到wac.refresh()这个方法,然后进入这个方法

springcolud源码解读与原理分析 spring源码讲解_spring_06

该方法就是整个spring的核心方法了,里面完成了BeanFactory的构建,本地配置文件application.xml的解析,Bean的创建,以及各种细节处理,例如:延迟加载,循环依赖……下面将源码贴出,附上每个方法的作用

@Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 执行刷新前先预处理
      prepareRefresh();

      /**
       * 获取BeanFactory;默认实现是DefaultListableBeanFactory
       * 加载BeanDefition 并注册到 BeanDefitionRegistry
       */
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // BeanFactory的预准备⼯作(BeanFactory进⾏一些设置,⽐如context的类加载器等)
      prepareBeanFactory(beanFactory);

      try {
        // BeanFactory准备⼯作完成后进⾏的后置处理⼯作
        postProcessBeanFactory(beanFactory);

        // 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
        invokeBeanFactoryPostProcessors(beanFactory);

        // 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
        registerBeanPostProcessors(beanFactory);

        // 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
        initMessageSource();

        // 初始化事件派发器
        initApplicationEventMulticaster();

        // ⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
        onRefresh();

        // 注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
        registerListeners();

        /**
         * 初始化所有剩下的⾮懒加载的单例bean
         * 初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
         * 填充属性
         * 初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
         * 调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
         */
        finishBeanFactoryInitialization(beanFactory);

        //完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件 ContextRefreshedEvent)
        finishRefresh();
      }

      catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
          logger.warn("Exception encountered during context initialization - " +
              "cancelling refresh attempt: " + ex);
        }

        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();

        // Reset 'active' flag.
        cancelRefresh(ex);

        // Propagate exception to caller.
        throw ex;
      }

      finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
      }
    }
  }

  我们来观察一下beanfatory的创建,下面我通过一张图来解析其创建流程

springcolud源码解读与原理分析 spring源码讲解_初始化_07

上图向我们展示了BeanFactory的创建过程,在此期间它解析了本地的application.xml配置文件,加载好各种属性,为接下来的Bean创建做准备。

三、小结

因篇幅有限,本篇就暂时解析到BeanFactory的创建过程。在这篇文章中,我们构建了一个简单的Spring项目用于研究Spring的源码,通过web.xml配置文件找到Spring启动的入口,接着用debug模式进一步了解其运行过程,然后我们代码跟踪了BeanFactory的创建过程。

研究源码的过程很枯燥,希望各位都能够在解读源码中完成自己的升华。