### 准备

## 目标

了解 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. python-social-auth with Django: ImportError: No module named 'social_django' 解决方法

    To use Django with python social auth, you need to install the Django app as well. You can specify t ...

  2. 170512、java日志文件log4j.properties配置详解

    一.Log4j配置 第一步:加入log4j-1.2.8.jar到lib下. 第二步:在CLASSPATH下建立log4j.properties.内容如下: 放在src下的话就不用配置 否则得去web. ...

  3. 焦作网络赛K-Transport Ship【dp】

    There are NN different kinds of transport ships on the port. The i^{th}ith kind of ship can carry th ...

  4. Database Sharding Challenges DATABASE SHARDING

    w分布式查询.数据聚合.跨碎片join是可且应避免的.自增主键管理.基于-会话/事务/语句-选择碎片.通过-主键/模块/碎片索引-碎片化数据 http://www.agildata.com/datab ...

  5. linux memory dump--http://www.forensicswiki.org/wiki/Tools:Memory_Imaging

    Linux provides two virtual devices for this purpose, '/dev/mem' and '/dev/kmem', though many distrib ...

  6. tomcat设置编码格式utf8

    利用request.setCharacterEncoding("UTF-8");来设置Tomcat接收请求的编码格式,只对POST方式提交的数据有效,对GET方式提交的数据无效! ...

  7. 【Python】sasa版:文件中csv读取在写入csv读取的数据和执行是否成功。

    sasa写的文件(包含解析文字) # coding=utf- from selenium import webdriver from time import sleep import keyword ...

  8. thinkcmf安装教程与目录结构详解 快速上手版

    最近接了一个建站项目,要求用thinkcmf来搭建,ytkah在想php都大致一样吧,快速地下载安装包,可是!怎么安装呢?没看到安装指引文件或目录,查看了安装说明public目录做为网站根目录,入口文 ...

  9. nodejs中使用linq

    官网地址 https://github.com/mihaifm/linq 安装 npm install linq 导入 var Enumerable = require(‘linq‘); 例子 1 总 ...

  10. 常微分方程初值问题:多步预测-修正方法 [MATLAB]

    #先上代码后补笔记# #可以直接复制粘贴调用的MATLAB函数代码!# 1. 亚当斯(Adams)预测-修正算法 由亚当斯-巴什福特(Adams-Bashforth)显式预测公式和亚当斯-莫顿(Ada ...