### 准备

## 目标

了解 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. npm安装出错的时候,如何使用国内的镜像!--解决办法

    在前端开发领域,Node已经很普遍了,使用Node就会使用到一些包.所以常用的 npm 就会经常使用得到,但是在使用 npm 安装一些包的过程中,会发现安装的速度会很慢,而且很多时候直接安装不了. 百 ...

  2. ssh-keygen 不是内部或外部命令解决办法!

    在使用 git 的远程仓库的时候,生成秘钥的使用,会遇到ssh-keygen不是内部命令也不是外部命令的问题: 具体解决: 第一步:找到:Git/usr/bin目录下的ssh-keygen.exe(一 ...

  3. Hibernate--快速上手

    一.初识 Hibernate 经典的软件应用体系结构有三层:表示层(提供了与用户交互的接口,实现用户操作界面,展示用户需要的数据).业务逻辑层(完成业务流程,处理表示层提交的数据请求,并将要保存的数据 ...

  4. 170623、springboot编程之JdbcTemplate操作数据库

    使用JdbcTemplate操作mysql数据库! 1.在pom中引入jpa包 <dependency> <groupId>org.springframework.boot&l ...

  5. Oracle管理监控之段空间利用监控-oracle性能优化

    SELECT S.OWNER,       S.SEGMENT_NAME,       S.SEGMENT_TYPE,       S.PARTITION_NAME,       ROUND(BYTE ...

  6. EOS主网节点部署

    EOS主网节点部署 #环境 ubuntu 16.4 EOS编译安装 EOS主网节点部署 eos区块查询.进程监控shell #EOS编译安装 #环境 ubuntu 16.4 # EOS安装部 # 以下 ...

  7. receive.denyCurrentBranch 推送错误解决

    场景: 1.搭建Ok了一git服务器 2.本机上的现有源码,现在想纳入git源码管理 操作: 1.服务器上创建了工程仓库 git init 2. 客户端使用tortoisegit添加并提交要纳入源码管 ...

  8. mysql 整数类型 数值类型 tinyint

    1.整数类型 整数类型:TINYINT SMALLINT MEDIUMINT INT BIGINT 作用:存储年龄,等级,id,各种号码等 ============================== ...

  9. Python学习开发资源大全列表

    1 机器学习和计算机视觉 Crab:灵活.快速的推荐引擎 gensim:人性化的话题建模库 hebel:GPU 加速的深度学习库 NuPIC:智能计算 Numenta 平台 pattern:Pytho ...

  10. Windows mysql默认字符集修改

    一.通过MySQL命令行修改: set character_set_client=utf8; set character_set_connection=utf8; set character_set_ ...