### 准备

## 目标

了解 bean reference 装配的流程

##测试代码

gordon.study.spring.ioc.IOC02_BeanReference.java
 
ioc02.xml
<beans ...>
    <bean id="chairman" class="gordon.study.spring.common.Employee">
        <property name="name" value="Cheque Wicket" />
        <property name="company" ref="macrohard" />
    </bean>
    <bean id="macrohard" class="gordon.study.spring.common.Company">
        <property name="name" value="macrohard" />
    </bean>

</beans>

### 分析

## BeanFactory.getBean 流程分析

当程序执行到第20行准备从 bean 容器中获取实例时,可以发现 DefaultListableBeanFactory 中已经成功读取到 BeanDefinition 信息:
List<String> beanDefinitionNames - [chairman, macrohard]
Map<String, BeanDefinition> beanDefinitionMap - {chairman=Generic bean: class [gordon.study.spring.common.Employee]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [ioc/ioc02.xml], macrohard=Generic bean: class [gordon.study.spring.common.Company]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [ioc/ioc02.xml]}
 
第21行 getBean 在本例中的核心流程如下(前4步同前一篇):
  1. 尝试从 Map<String, Object> singletonObjects 中获取名字为 chairman 的 bean 实例,当然,获取不到。
  2. 将 bean 标记为已创建(或即将创建完成)状态。就是将 chairman 放到 Set<String> alreadyCreated 中。
  3. 根据 BeanDefinition 生成 RootBeanDefinition。- Root bean: class [gordon.study.spring.common.Employee]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [ioc/ioc02.xml]
  4. 将 RootBeanDefinition 放到 Map<String, RootBeanDefinition> mergedBeanDefinitions 中。
  5. 根据 RootBeanDefinition 解析出 bean class,对于本例,是把 bean class name "gordon.study.spring.common.Employee" 解析为对应的 Class 实例。
    BeanDefinition 有个属性 Object beanClass 用来记录 bean 定义对应的类类型,在前4步,其值为字符串 "gordon.study.spring.common.Employee",现在通过 resolveBeanClass 方法,将之修改为 Employee 类对应的 Class 实例。
    实际的解析工作交给工具类 org.springframework.util.ClassUtils 的 forName 方法(暂不深入,调用栈见下图)。
    通过 forName 方法得到 Class 实例后,框架将 Class 实例赋给 Object beanClass 属性以取代原来的字符串。这样,对于该 BeanDefinition,以后就可以直接获得其对应的 Class 实例了。
  6. 解析出用来创建实例的构造器,设置成 BeanDefinition 的 Object resolvedConstructorOrFactoryMethod 属性。对于本例,是默认构造函数。
  7. 使用该构造器通过反射机制创建 Employee 实例。
  8. 用 org.springframework.beans.BeanWrapper 包装新创建的 Employee 实例。
  9. 接下来要装配属性。populateBean 方法负责装配属性。
    BeanDefinition 的 MutablePropertyValues propertyValues 属性在解析 XML 配置文件时就已经设置好。对于本例,配置文件中的 chairman bean 的 name 和 company 属性分别对应一个 PropertyValue 存在于 MutablePropertyValues propertyValues 的 List<PropertyValue> propertyValueList 属性中。
    org.springframework.beans.PropertyValue 的核心属性是 String name 和 Object value,其中 value 有多种可能类型,在本例中,name 属性对应的 PropertyValue 的 value 是 org.springframework.beans.factory.config.TypedStringValue 类型(TypedStringValue: value [Cheque Wicket], target type [null]);company 属性对应的 PropertyValue 的 value 是 org.springframework.beans.factory.config.RuntimeBeanReference 类型(<macrohard>)。
  10. 接下来会创建一个帮助类 org.springframework.beans.factory.support.BeanDefinitionValueResolver,它的 resolveValueIfNecessary 方法可以将指定 BeanFactory 的指定 BeanDefinition 中的配置属性值转化为实际值。resolveValueIfNecessary 方法根据不同的值类型(PropertyValue 对象中 Object value 的实际类型)调用不同的处理流程解析属性值。
  11. 遍历 MutablePropertyValues propertyValues 中所有的 PropertyValue,通过 BeanDefinitionValueResolver 计算出转换后的实际值。对于 name 属性,它计算出的值就是 TypedStringValue 的 value:Cheque Wicket;对于 company 属性,它判断出类型是 RuntimeBeanReference,因此调用 BeanFactory 的 getBean 方法尝试获取 Company 的 singleton bean 实例,递归解析 bean reference 的依赖关系(见上图调用栈)。
  12. 当解析出所有属性实际值后,通过 org.springframework.beans.BeanWrapperImpl 的 setPropertyValues 方法为所有属性赋值。这部分代码原理很简单,对每个 PropertyValue,找出对应的 setter 方法,通过反射为属性赋值。但是实际代码很复杂,涉及到属性名的 token 化表示(例如 company.email 这种类型),以及如何确定 setter 方法等等细节问题,暂时不深入研究。
  13. 调用 initializeBean 方法初始化 bean 实例,包括响应各种 Aware 接口、处理 bean post processor 以及调用 init 方法等。
 
简而言之,上面涉及到 Spring IOC 容器创建 bean 的三个步骤:
  • 5~7:实例化 bean
  • 8~12:装配 bean
  • 13:初始化 bean
 
 
 
 
 
 

Spring IOC 源码简单分析 02 - Bean Reference的更多相关文章

  1. Spring IOC 源码简单分析 04 - bean的初始化

      ### 准备 ## 目标 了解 Spring 如何初始化 bean 实例 ##测试代码 gordon.study.spring.ioc.IOC04_Initialization.java publ ...

  2. Spring IOC 源码简单分析 03 - 循环引用

    ### 准备 ## 目标 了解 Spring 如何处理循环引用 ##测试代码 gordon.study.spring.ioc.IOC03_CircularReference.java   ioc03. ...

  3. Spring IOC 源码简单分析 01 - BeanFactory

    ### 准备 ## 目标 了解 Spring IOC 的基础流程 ## 相关资源 Offical Doc:http://docs.spring.io/spring/docs/4.3.9.RELEASE ...

  4. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  5. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

  6. Spring Ioc源码分析系列--容器实例化Bean的四种方法

    Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...

  7. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  8. spring IoC源码分析 (3)Resource解析

    引自 spring IoC源码分析 (3)Resource解析 定义好了Resource之后,看到XmlFactoryBean的构造函数 public XmlBeanFactory(Resource  ...

  9. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

随机推荐

  1. angular中对象与字符串之间的转换

    1.angular 里 字符串与对象互转  angular.toJson();将字符串转成对象 angular.forJson(); 将字符串转成对象  2.angular 循环    <scr ...

  2. Eclipse For Android 代码自动提示功能

    Eclipse for android 实现代码自动提示智能提示功能,介绍 Eclipse for android 编辑器中实现两种主要文件 java 与 xml 代码自动提示功能,解决 eclips ...

  3. scrapy爬虫系列之开头--scrapy知识点

    介绍:Scrapy是一个为了爬取网站数据.提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速抓取.Scrapy使用了Twisted异步网络框架,可以加快我们的下载速度. 0.说明: ...

  4. 深入理解Docker容器执行引擎runC

    1 简介 根据官方的定义:runC是一个根据OCI标准创建并运行容器的CLI tool. Docker就是基于runC创建的,简单地说,runC就是docker中最为核心的部分,容器的创建,运行,销毁 ...

  5. RSA与AES的区别

    RSA 非对称加密,公钥加密,私钥解密,反之亦然.由于需要大数的乘幂求模等算法,运行速度慢,不易于硬件实现. 通常私钥长度有512bit,1024bit,2048bit,4096bit,长度越长,越安 ...

  6. Mysql中的auto_increment

    Mysql中的auto_increment 1.创建 2.使用 [1]如果不写固定列,则必须要插入该列,可以直接写Null,否则会报错 [2]可以直接在auto_increment 列上直接插入显式值 ...

  7. C#+GDAL读写文件

    读取shp文件: private void btnBrower_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFil ...

  8. xmr monero miner

    https://github.com/fireice-uk/xmr-stak-cpu xmr-stak-cpu安装 xmr-stak-cpu是一个用于cpu计算的开源软件,下面记录在ubuntu17. ...

  9. Web安全大揭秘

    web安全大揭秘,通常会有那些web安全问题呢? 1,xss 2,sql注入 3,ddos攻击

  10. java猫和猫的名字

    这篇文章之所以叫猫和猫的名字,是因为是以猫为案例来讲的 主要的内容就是java构造函数和参数的传递 class Animal { public static String name; Animal(S ...