Spring源码剖析一(BeanFactory的创建)

一、概述

从事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,读者朋友可自行下载)

项目已经构建完毕,现在我们先看下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>标签配置的内容,我们便以这个为入口,去源码里面一探究竟……

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

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

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

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

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

该方法就是整个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的创建,下面我通过一张图来解析其创建流程

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

三、小结

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

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


感兴趣的读者朋友可以 关注本公众号,和我们一起学习探究。



本人因所学有限,如有错误之处,望请各位指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值