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

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

大家好,又见面了,我是你们的朋友全栈君。

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

整个过程分为四个步骤

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

parseBeanDefinitionElement()

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

  • 解析 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 实例

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

BeanDefinition 整个结构如下图:

    f30720e9703a84828c920db9570bbd69b02.jpg

  • 父 <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 标签基本属性解析后

  • 会依次调用 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 实现

  • AttributeAccessorSupport 是接口 AttributeAccessor 的实现者
  • AttributeAccessor 接口定义了与其他对象的元数据进行连接和访问的约定,可以通过该接口对属性进行获取、设置、删除操作

lookup-method 子元素

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

举个栗子:

ef5a49854371c22ba0da9f4f1f848747684.jpg

  • 配置如下

75e68c1a323f31ee6c96276a39c98c16e4c.jpg

replace-method 子元素

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

举个栗子:

3b4c7a417d7e62727039346890bb76c7b45.jpg

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

8db291580b451f9a99ed4b1b6d92a474ee4.jpg

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

123dad2aecda5a12af18c4f5b3d7133b304.jpg

constructor-arg 子元素

29dad20d991fb118c304b1c6541c67b4deb.jpg

  • 首先获取 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 子元素

a45424a08bbae59b899921a2522a6264c5f.jpg

Spring 调用 parsePropertyElements() 

转载于:https://my.oschina.net/u/3847203/blog/2250724

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/107243.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号