Spring IOC 之解析 bean 标签:开启解析进程,BeanDefinition

在方法 parseDefaultElement() 中,若是遇到标签 为 bean 则调用 processBeanDefinition() 方法进行 bean 标签解析java

整个过程分为四个步骤spring

  • 调用 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 进行元素解析,解析过程当中若是失败,返回 null,错误由 ProblemReporter 处理。若是解析成功则返回 BeanDefinitionHolder 实例 bdHolder。BeanDefinitionHolder 为持有 name 和 alias 的 BeanDefinition。
  • 若实例 bdHolder 不为空,则调用 BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired() 进行自定义标签处理
  • 解析完成后,则调用 BeanDefinitionReaderUtils.registerBeanDefinition() 对 bdHolder 进行注册
  • 发出响应事件,通知相关的监听器,完成 Bean 标签解析

parseBeanDefinitionElement()架构

没有对 Bean 标签进行解析,只是在解析动做以前作了一些功能架构,主要的工做有:ide

  • 解析 id、name 属性,肯定 alias 集合,检测 beanName 是否惟一
  • 调用方法 parseBeanDefinitionElement() 对属性进行解析并封装成 GenericBeanDefinition 实例 beanDefinition
  • 根据所获取的信息(beanName、aliases、beanDefinition)构造 BeanDefinitionHolder 实例对象并返回

这里有必要说下 beanName 的命名规则:函数

  • 若是 id 不为空,则 beanName = id;
  • 若是 id 为空,可是 alias 不空,则 beanName 为 alias 的第一个元素,若是二者都为空,则根据默认规则来设置 beanName

BeanDefinition

  • 解析 bean 标签的过程其实就是构造一个 BeanDefinition 对象的过程
  • <bean> 元素标签拥有的配置属性,BeanDefinition 均提供了相应的属性,与之一一对应
  • 因此咱们有必要对 BeanDefinition 有一个总体的认识

BeanDefinition 是一个接口,它描述了一个 Bean 实例ui

  • 包括属性值、构造方法值和继承自它的类的更多信息
  • 它继承 AttributeAccessor 和 BeanMetadataElement 接口
    • AttributeAccessor :定义了与其它对象的(元数据)进行链接和访问的约定,即对属性的修改,包括获取、设置、删除
    • BeanMetadataElement:Bean 元对象持有的配置元素能够经过getSource() 方法来获取

BeanDefinition 整个结构以下图:this

    

  • 父 <bean> 用 RootBeanDefinition表示
  • 子 <bean> 用 ChildBeanDefinition 表示
  • 而没有父 <bean> 的就使用RootBeanDefinition 表示。GenericBeanDefinition 为一站式服务类
  • AbstractBeanDefinition对三个子类共同的类信息进行抽象。

解析 Bean 标签

  • 在 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 中完成 Bean 的解析
    • 返回的是一个已经完成对 <bean> 标签解析的 BeanDefinition 实例
    • 在该方法内部,首先调用 createBeanDefinition() 方法建立一个用于承载属性的 GenericBeanDefinition 实例
    • 委托 BeanDefinitionReaderUtils 建立

 

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
			throws ClassNotFoundException {
		return BeanDefinitionReaderUtils.createBeanDefinition(
				parentName, className, this.readerContext.getBeanClassLoader());
	}
  • 建立完 GenericBeanDefinition 实例后,再调用 parseBeanDefinitionAttributes() 
  • 该方法将建立好的 GenericBeanDefinition 实例当作参数,对 Bean 标签的全部属性进行解析

完成 Bean 标签基本属性解析后spa

  • 会依次调用 parseMetaElements()、parseLookupOverrideSubElements()、parseReplacedMethodSubElements() 对子元素 meta、lookup-method、replace-method 完成解析

三个子元素的做用以下:设计

  • meta:元数据。
  • lookup-method:Spring 动态改变 bean 里方法的实现。方法执行返回的对象,使用 Spring 内原有的这类对象替换,经过改变方法返回值来动态改变方法。内部实现为使用 cglib 方法,从新生成子类,重写配置的方法和返回对象,达到动态改变的效果。
  • replace-method:Spring 动态改变 bean 里方法的实现。须要改变的方法,使用 Spring 内原有其余类(须要继承接口org.springframework.beans.factory.support.MethodReplacer)的逻辑,替换这个方法。经过改变方法执行逻辑来动态改变方法。

meta 子元素

  • meta :元数据。当须要使用里面的信息时能够经过key获取
  • meta 所声明的 key 并不会在 Bean 中体现,只是一个额外的声明,当咱们须要使用里面的信息时,经过 BeanDefinition 的 getAttribute()
  • 解析过程较为简单,获取相应的 key - value 构建 BeanMetadataAttribute 对象,而后经过 addMetadataAttribute() 加入到 AbstractBeanDefinition

委托 AttributeAccessorSupport 实现code

  • AttributeAccessorSupport 是接口 AttributeAccessor 的实现者
  • AttributeAccessor 接口定义了与其余对象的元数据进行链接和访问的约定,能够经过该接口对属性进行获取、设置、删除操做

lookup-method 子元素

  • lookup-method :获取器注入,是把一个方法声明为返回某种类型的 bean 但实际要返回的 bean 是在配置文件里面配置的
  • 该方法能够用于设计一些可插拔的功能上,解除程序依赖

举个栗子:

  • 配置以下

replace-method 子元素

  • replaced-method :能够在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑
  • 该标签使用方法和 lookup-method 标签差很少,只不过替代方法的类须要实现 MethodReplacer 接口

举个栗子:

  • 配置以下(执行原始方法):

  • 配置以下(执行替换方法):

constructor-arg 子元素

  • 首先获取 index、type、name 三个属性值,而后根据是否存在 index 来区分
  • 其实二者逻辑都差很少,总共分为以下几个步骤(以有 index 为例)
    • 构造 ConstructorArgumentEntry 对象并将其加入到 ParseState 队列中。ConstructorArgumentEntry 表示构造函数的参数
    • 调用 parsePropertyValue() 解析 constructor-arg 子元素,返回结果值
    • 根据解析的结果值构造 ConstructorArgumentValues.ValueHolder 实例对象
    • 将 type、name 封装到 ConstructorArgumentValues.ValueHolder 中,而后将 ValueHolder 实例对象添加到 indexedArgumentValues 中

parsePropertyValue() 对子元素进一步解析

  1. 提取 constructor-arg 子元素的 ref 和 value 的属性值,对其进行判断,如下两种状况是不容许存在的
    • ref 和 value 属性同时存在
    • 存在 ref 或者 value 且又有子元素
  2. 若存在 ref 属性,则获取其值并将其封装进 RuntimeBeanReference 实例对象中
  3. 若存在 value 属性,则获取其值并将其封装进 TypedStringValue 实例对象中
  4. 若是子元素不为空,则调用 parsePropertySubElement() 进行子元素进一步处理

须要调用 parsePropertySubElement() 进一步处理

property 子元素

Spring 调用 parsePropertyElements() 

相关文章
相关标签/搜索