• Spring IOC 源码简单分析 02


    ### 准备

    ## 目标

    了解 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.ClassUtilsforName 方法(暂不深入,调用栈见下图)。
      通过 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.BeanWrapperImplsetPropertyValues 方法为所有属性赋值。这部分代码原理很简单,对每个 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
     
     
  • 相关阅读:
    Linux(一)简介与安装
    BBS项目(四)
    BBS项目(三)
    BBS项目(二)
    BBS项目(一)
    会话控制
    SQL表连接查询
    [转]使用GROUP BY WITH ROLLUP改善统计性能
    MySQL中的set和enum
    PHP操作MySQL
  • 原文地址:https://www.cnblogs.com/gordonkong/p/7358841.html
Copyright © 2020-2023  润新知