spring——bean的创建过程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring——bean的创建过程相关的知识,希望对你有一定的参考价值。

  1. spring的核心容器包括:core、beans、context、express language四个模块。所以对于一个简单的spring工程,最基本的就是依赖以下三个jar包即可:
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.8.RELEASE</version>
        </dependency>

    暂时先不考虑express language模块。

  2. 通过xml文件创建一个spring bean的大概过程
    1. 主要考虑以下情况:
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <bean id="myTestBean" class="chapter02.MyTestBean"/>
      
      </beans>
      BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

      注:beanFactoryTest.xml指上述xml文件;chapter02.MyTestBean是一个简单的bean(包含一个私有属性,get,set方法)

    2. 关于new ClassPathResource("beanFactoryTest.xml")
      1. 接口InputStreamSource封装任何能返回InputStream的类。它只有一个方法定义:getInputStream();该方法返回一个InputStream对象。
      2. Resource接口继承了InputStreamSource接口
      3. ClassPathResource类是Resource接口的一个实现类
    3. 关于new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
      1. 首先对传入的resource参数做封装
      2. 其次通过SAX读取XML文件的方式来准备InputSource对象
      3. 最后将准备好的数据通过参数传入真正的核心处理部分:doLoadBeanDefinitions
    4. 对于真正的核心处理部分:doLoadBeanDefinitions
      1. 获取对XML文件的验证模式:DTD、XSD
      2. 加载XML文件,并得到对应的Document。通过SAX解析XML文档的步骤如下:
        1. 首先创建DocumentBuilderFactory
        2. 通过DocumentBuilderFactory创建DocumentBuilder
        3. 使用DocumentBuilder解析InputSource来返回Document对象
      3. 根据返回的Document注册Bean信息
        1. BeanDefinitionDocumentReader接口的实现类DefaultBeanDefinitionDocumentReader会获取到document对象的root节点
        2. 通过root节点获取所有的bean节点:一种是默认的bean节点:使用parseDefaultElement();方法进行处理;一种是自定义的bean节点:使用delegate.parseCustomElement()方法进行处理,其中delegate是根据自定义节点的命名空间获取到的自定义的节点处理器;以下只讨论对于第一种情况即:对于默认的bean节点的处理。
        3. 对于默认的bean节点有四种标签:import、alias、bean、beans,我们这里主要讨论最常用也最复杂的bean标签:
          1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement();方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder
            1. 提取元素中的id以及name属性
            2. 进一步解析其他所有属性以及子元素,并统一封装至GenericBeanDefinition类型的实例中;注:BeanDefinition存在三个实现类:RootBeanDefinition(父节点)、ChildBeanDefinition(子节点)、GenericBeanDefinition(Xml属性对应的java容器)
              1. 解析各种属性:scope属性、singleton属性、abstract属性、lazy-init属性、autowire属性、dependency-check属性、depends-on属性、autowire-candidate属性、primary属性、init-method属性、destory-method属性、factory-method属性、factory-bean属性
              2. 解析各种子元素:
                1. 解析元数据:parseMetaElements
                2. 解析lookup-method子元素:parseLookupOverrideSubElements
                3. 解析replaced-method子元素:parseReplacedMethodSubElements
                4. 解析构造函数参数:parseConstructorArgElements
                5. 解析property子元素:parsePropertyElements
                6. 解析qualifier子元素:parseQualifierElements
            3. 如果检测到bean没有指定beanName,那么使用默认规则为此bean生产beanName
            4. 将获取到的信息封装到BeanDefinitionHolder的实例中
          2. 当返回的bdHolder下存在自定义属性时,还需要对自定义标签进行解析。例如以下包含mybean:user自定义属性时:
            <bean id="test" class="test.Myclass">
                    <mybean:user username="aaa"/>
            </bean>

            需要用到delegate.decorateBeanDefinitionIfRequired();进行自定义标签的处理,处理过程如下:

            1. 遍历所有的属性以及所有的子元素
            2. 根据节点获取标签的命名空间
            3. 根据命名空间判断是否是默认标签,这里只对非默认标签进行处理,因为对于默认标签在之前已经处理过了
            4. 根据命名空间找到对应的处理器
            5. 使用相应的处理器进行修饰处理
          3. 对解析完成后的bdHolder进行注册:包括通过beanName的注册以及alias(别名)的注册
            1. 通过beanName注册BeanDefinition:this.beanDefinitionMap.put(beanName, beanDefinition)
              1. 对AbstractBeanDefinition的methodOvirrides属性进行校验
              2. 如果beanName已经注册过:如果用户设置了不允许重复注册,则抛异常,否则直接覆盖,重新注册
              3. 加入beanDfinitionMap
              4. 清除解析之前留下的对应beanName的缓存
            2. 通过alias(别名)注册BeanDefinition:this.aliasMap.put(alias, name);
              1. alias与beanName相同,则不需要处理,并删除掉原有alias
              2. 若aliasName存在,根据用户是否允许别名覆盖进行相应的处理
              3. alias循环检查。例如:当存在A(key)-->B(value)时,如果还存在B(key)-->A(value)或者B(key)-->C(value)、C(key)-->A(value)时,称为出现了循环。此时则会抛出异常
              4. 注册alias:this.aliasMap.put(alias, name);
          4. 最后发出响应事件,通知相关的监听器  





以上是关于spring——bean的创建过程的主要内容,如果未能解决你的问题,请参考以下文章

老王读Spring IoC-3Spring bean 的创建过程

Spring创建Bean的顺序

spring中bean创建过程中的扩展点

spring中bean创建过程中的扩展点

Spring创建Bean的过程

Spring_Spring与IoC_Bean的装配