Spring源码深度解析 默认标签的解析总结

默认标签的解析是在 parseDefaultElement 函数中进行的,分别对4
种不同标签( import alias bean 和 beans ) 做了不同的处理
在这里插入图片描述

bean标签的解析及注册
在这里插入图片描述
在这里插入图片描述
首先委托 BeanDefinitionDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,
返回 BeanDefinitionHolder 类型的实例 bdHolder 经过这个方法后, bdHolder 实例已经包含我们配置
文件中配置的各种属性了,例如 class name id alias 之类的属性
当返回的 bdHolder 不为空的情况下 若存在默认标签的子节点下再有自定义属性 还需要
再次对自定义标签进行解析
解析完成后, 需要对解析后的 bdHolder 进行注册,同样, 册操操作委托给了 BeanDefinitionReaderUtils的registerBeanDefinition 方法
最后发出响应事件,通知相关的监听器,这个 bean 已经加载完成了
在这里插入图片描述

解析 BeanDefinition
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在开始对属性展开全面解析前, Spring 在外层又做了一个当前层的功能架构,在当前层完成的
主要工作包括如下内容
提取元素中的 id 以及 name 属性
2. 进一步解析其他所有属性并统一封装至 GenericBeanDefinition 类型的实例中
如果检测到 bean 没有指定 beanName ,那么使用默认规则为此 Bean 生成 beanName
将获取到的信息封装到 BeanDefinitionHolder 的实例中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建用于属性承载的 BeanDefinition
BeanDefinition 是一个接口,在 Spring 中存在三种实现: RootBeanDefinition ChildBeanDefinition 以及 GenericBeanDifinition
三种实现均继承了 abstractBeanDefiniton ,其中
BeanDefinition 是配置文件<bean>元素标签在容器中的内部表示形式 <bean>元素标签拥有
class scope lazy-init 等配置属性, BeanDefinition 则提供了相应的 beanClass scope lazyInit
属性, BeanDefinition 和<bean>中的属性是一一对应的 其中RootBeanDefinition 是最常用的实现
类,它对应一般性的<bean>元素标签, GenericBeanDefinition 是自 2.5 版本以后新加入的 bean
文件配置属性定义类,是一站式服务类
在配置文件中可以定义父<bean>和子<bean>,父<bean>用 RootBeanDefinition 表示,而子
<bean>用 ChildBeanDefiniton 表示,而没有父<bean>的就使用 RootBeanDefinition 表示
AbstractBeanDefinition 对两者共同的类信息进行抽象
Spring 通过 BeanDefinition 将配置文件中的<bean >配置信息转换为容器的内部表示,并将
这些 BeanDefiniton 注册到 BeanDefinitonRegistry中 Spring 容器的 BeanDefinitionRegistrγ 就像是
Spring 配置信息的内存数据库,主要是以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息
在这里插入图片描述
要解析属性首先要创建用于承载属性的实例 ,也就是创建 GenericBeanDefinition
类型的实例 而代码 createBeanDefinition(className, parent)的作用就是实现此功能
在这里插入图片描述
在这里插入图片描述
解析各种属性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解析子元素 meta
在这里插入图片描述
这段代码并不会体现在 MyTestBean 的属性当中,而是一个额外的声明,当需要使用里面
的信息的时候可以通过 BeanDefinition的getAttribute(key)方法进行获取
在这里插入图片描述
在这里插入图片描述
解析子元素 lookup-method
子元素 lookup-method 似乎并不是很常用,但是在某些时候它的确是非常有用的
属性,通常我们称它为获取器注入
获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的 bean ,但实际要返回的 bean 是在配
置文件里面配置的,此方法可用在设计有些可插拔的功能上,解除程序依赖
在这里插入图片描述
在这里插入图片描述
抽象方法还没有被实现,怎么可以直接调用呢?答案就在 Spring 为我们
提供的获取器中
在这里插入图片描述
这个配置完成的功能是动态地将teacher 所代表的 bean 作为 getBean 的返回值,运行测试方法我们会看到控制台上的输出
i am Teacher
当我们的业务变更或者在其他情况下, teacher 里面的业务逻辑已经不再符合我们的业务要
求,需要进行替换怎么办呢?这是我们需要增加新的逻辑类
在这里插入图片描述
在这里插入图片描述
再次运行测试类,会发现不一样的结果:i am Student

在这里插入图片描述
上面的代码与 parseMetaElements 的代码大同小异,最大的区别就是在if判
断中的节点名称在这里被修改为 LOOKUP_METHOD_ELEMENT 还有,在数据存储上面通过
使用 LookupOverride 类型的实体类来进行数据承载并记录在 AbstractBeanDefinition 中的
methodOverrides 属性中

解析子元素replaced-method
方法替换:可以在运行时用新的方法替换现有的方法 与之前的 look-up 不同的是,
replaced-method 不但可以动态地替换返回实体 bean ,而且还能动态地更改原有方法的逻辑
在这里插入图片描述
在这里插入图片描述
运行测试类就可以看到预期的结果了,控制台成功打印出“我替换了原有的方法” 也就是说做到了动态替换原有方法
再来看元素的提取过程
在这里插入图片描述
在这里插入图片描述
可以看到无论是 look-up还是replaced-method 都是构造了一个 MethodOverride ,并最
终记录在了 AbstractBeanDefinition 中的 methodOverrides 属性中

解析子元素 constructor-arg
在这里插入图片描述
上面的配置是 Spring 构造函数配置中最基础的配置,实现的功能就是对 HelloBean 自动寻
找对应的构造函数,并在初始化的时候将设置的参数传入进去
对于 constructo-arg 子元素的解析, Spring 是通过 parseConstructorArgElements 函数来实现

在这里插入图片描述
遍历所有子元素,提取所有 constructor-atg ,然
后进行解析,具体的解析被放置在了另个函数 parseConstructorArgElement 中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
首先是提取 constructor-arg 上必要的属性( index type name )
如果配置中指定了 index 属性,那么操作步骤如下
解析 Constructor-arg 的子元素
使用 ConstructorArgumentValues. ValueHolder 类型来封装解析出来的元素。
将type name index 属性一并封装在 ConstructorArgumentValues. ValueHolder 类型中
并添加至当前 BeanDefinition的constructorArgumentValues的indexedArgumentValues 属性中
如果没有指定 index 属性,那么操作步骤如下
解析 constructor-arg 的子元素
使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素
将type name和index属性一并封装在 ConstructorArgurnentValues.ValueHolder 类型中
并添加至当前 BeanDefinition的constructorArgumentValues的genericArgumentValues 属性中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对构造函数中属性元素的解析,经历了以下几个过程
略过 description 或者 meta
提取 constructor-arg 上的 ref 和 value 属性,以便于根据规则验证正确性,其规则为在
constructor-arg 上不存在以下情况
同时既有 ref 属性又有 value 属性
存在 ref 属性或者 value 属性且又有子元素
ref 属性的处理 使用 RuntimeBeanReference 封装对应的 ref 名称,如:
<constructor-arg ref=" a ” >
value 属性的处理 使用 TypedStringValue 封装,如:
<constructor-arg value=” a ” >
子元素的处理,如


<entry key=” key ” value=” value ” />

</constructor- arg>

parsePropertySubElement 中实现了对各种子元素的分类处理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解析子元素 property
parsePropertyElement 函数完成了对 property 属性的提取
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面函数与构造函数注入方式不同的是将返回值使用 PropertyValue 进行封装,并记录
在了 BeanDefinition 中的 propertyValues 属性中

解析子元素 qualifier
在使用 Spring 框架中进行自动注入时, Spring 容器中匹配的候选 Bean 数目必须有且仅有一个 当找不到一个匹配的 Bean 时,
Spring 容器将抛出 BeanCreationException 异常, 指出必须至少拥有一个匹配的 Bean
Spring 允许我们通过 Qualifier 指定注入 Bean 的名称, 这样歧义就消除了
在这里插入图片描述

AbstractBeanDefinition 属性
GenericBeanDefinition 只是子类实现,而大部分的通用属性都保存在了 AbstractBeanDefinition

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解析默认标签中的自定义标签元素
在这里插入图片描述

bdHolder = delegate.decorateBeanDefinitionlfRequired(ele, bdHolder)
这句代码适用于这样的场景
在这里插入图片描述
当Spring 中的 bean 使用的是默认的标签配置,但是其中的子元素却使用了 自定义的配置
时,这句代码便会起作用了
在这里插入图片描述
第三个参数是父类 bean ,当对某个嵌套配置进行分析时 ,这里需要传递父类
beanDefinition 分析源码得知这里传递的参数其实是为了使用父类的 scope 属性,以备子类若
没有设置 scope 时默认使用父类的属性,这里分析的是顶层配置 所以传递 null 将第三个参
数设置为空后进一步跟踪函数
在这里插入图片描述
函数分别对元素的所有属性以及子节点进行了 decoratelfRequired函数的调用
在这里插入图片描述
在这里插入图片描述
首先获取属性或者元素的命名空间 以此
来判断该元素或者属性是否适用于自定义标签的解析条件, 找出自定义类型所对应的
NamespaceHandler 并进行进一步解析
在 decorateBeanDefinitionlfRequired中我们可以看到对于程序默认的标签的处理其实是直接略过的 ,因为默认的标签到这里已经被处
理完了, 这里只对自定义的标签或者 bean 的自定义属性感兴趣 在方法中实现了寻找自定义
标签并根据自定义标签寻找命名空间处理器, 进行进一步的解析

注册解析的 BeanDefinition
在这里插入图片描述
在这里插入图片描述
解析的 beanDefinition 都会被注册到 BeanDefinitionRegistry 类型
的实例 registry 中, 而对于 beanDefinition 的注册分成了两部分:通过 beanName 的注册以及通过别名的注册

通过 beanName 注册 BeanDefinition
在这里插入图片描述
在这里插入图片描述
对AbstractBeanDefinition 的校验 在解析 XML 文件的时候我们提过校验,但是此校验
非彼校验,之前的校验时针对于 XML 格式的校验,而此时的校验时针是对于 AbstractBeanDefinition的methodOverrides 属性的
对beanName 已经注册的情况的处理 如果设置了不允许 bean 的覆盖,则需要抛出
异常,否则直接覆盖
加入 map 缓存
清除解析之前留下的对应 beanName 的缓存

通过别名注册 BeanDefinition
在这里插入图片描述
在这里插入图片描述
alias与beanName 相同情况处理 若alias与beanName 并名称相同则不需要处理并删
除掉原有 alias
alias 覆盖处理 若aliasName 已经使用并已经指向了另一beanName 则需要用户的设置
进行处理
alias 循环检查 当A->B 存在时,若再次 A->C->B 时候则会抛出异常
注册 alias

通知监听器解析及注册完成
通过代码 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))
完成此工作,这里的实现只为扩展,当程序开发人员需要对注册 BeanDefinition 事件进行监听
时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在 Spring 中并没有对此事件做
任何逻辑处理

alias 标签的解析
在对 bean 进行定义时,除了使用 id 属性来指定名称之外,为了提供多个名称,可以使用
alias 标签来指定 而所有的这些名称都指向同一个 bean ,在某些情况下提供别名非常有用,比
如为了让应用的每一个组件能更容易地对公共组件进行引用
然而,在定义 bean 时就指定所有的别名并不是总是恰当的 有时我们期望能在当前位置
为那些在别处定义的 bean 引人别名 在XML配置文件中,可用单独的<alias/>元素来完成
bean 别名的定义 如配置文件中定义了一个Java Bean:
<bean id= ”testBean ” class= ”com.test ”/>
要给这个 JavaBean 增加别名,以方便不同对象来调用 我们就可以直接使用 bean 标签中的
name 属性:
在这里插入图片描述
Spring 还有另外一种声明别名的方式:
在这里插入图片描述
考虑一个更为具体的例子 ,组件A在 XML 配置文件中定义了一个名为 componentA的
DataSource 类型的 bean ,但组件B 却想在其 XML 文件中以 componentB 命名来引用此 bean
而且在主程序 MyApp的XML配置文件中,希望以 myApp 的名字来引用此 bean 最后容器加载3个
XML 文件来生成最终的 ApplicationContext 在此情形下,可通过在配置文件中添加下列
alias 元素来实现:
在这里插入图片描述
alias 标签的解析过程
在这里插入图片描述

import 标签的解析
在这里插入图片描述
applicationContext.xml 文件中使用import的方式导人有模块配置文件,以后若有新模块的
加入,那就可以简单修改这个文件了 这样大大简化了配置后期维护的复杂度 并使配置模块
化, 易于管理
在这里插入图片描述
在这里插入图片描述
获取 resource 属性所表示的路径
解析路径中的系统属性,格式如“${user.dir}”
判定 location 是绝对路径还是相对路径
如果是绝对路径则递归调用 bean 的解析过程,进行另一次的解析
果是相对路径则计算出绝对路径并进行解析
通知监听器,解析完成

嵌入式 beans 标签的解析
在这里插入图片描述
与单独的配置文件并没有太大的差别,无非是递归调用 beans 的解析过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值