大家好,又见面了,我是你们的朋友全栈君。
在方法 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 整个结构如下图:
- 父 <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 是在配置文件里面配置的
- 该方法可以用于设计一些可插拔的功能上,解除程序依赖
举个栗子:
- 配置如下
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()
对子元素进一步解析
- 提取 constructor-arg 子元素的 ref 和 value 的属性值,对其进行判断,以下两种情况是不允许存在的
- ref 和 value 属性同时存在
- 存在 ref 或者 value 且又有子元素
- 若存在 ref 属性,则获取其值并将其封装进 RuntimeBeanReference 实例对象中
- 若存在 value 属性,则获取其值并将其封装进 TypedStringValue 实例对象中
- 如果子元素不为空,则调用 parsePropertySubElement() 进行子元素进一步处理
需要调用 parsePropertySubElement()
进一步处理
property 子元素
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账号...