Spring 源码第 8 篇,各种属性的解析

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  1. Spring 源码解读计划

  2. Spring 源码第一篇开整!配置文件是怎么加载的?

  3. Spring 源码第二弹!XML 文件解析流程

  4. Spring 源码第三弹!EntityResolver 是个什么鬼?

  5. Spring 源码第四弹!深入理解 BeanDefinition

  6. 手把手教你搭建 Spring 源码分析环境

  7. Spring 源码第六弹!松哥和大家聊聊容器的始祖 DefaultListableBeanFactory

  8. Spring 源码解读第七弹!bean 标签的解析

1.解析方法回顾


上篇文章我们最终分析到下面这个方法:

@Nullable

public AbstractBeanDefinition parseBeanDefinitionElement(

Element ele, String beanName, @Nullable BeanDefinition containingBean) {

this.parseState.push(new BeanEntry(beanName));

String className = null;

if (ele.hasAttribute(CLASS_ATTRIBUTE)) {

className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

}

String parent = null;

if (ele.hasAttribute(PARENT_ATTRIBUTE)) {

parent = ele.getAttribute(PARENT_ATTRIBUTE);

}

try {

AbstractBeanDefinition bd = createBeanDefinition(className, parent);

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

parseMetaElements(ele, bd);

parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

parseConstructorArgElements(ele, bd);

parsePropertyElements(ele, bd);

parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());

bd.setSource(extractSource(ele));

return bd;

}

catch (ClassNotFoundException ex) {

error(“Bean class [” + className + “] not found”, ele, ex);

}

catch (NoClassDefFoundError err) {

error(“Class that bean class [” + className + “] depends on not found”, ele, err);

}

catch (Throwable ex) {

error(“Unexpected failure during bean definition parsing”, ele, ex);

}

finally {

this.parseState.pop();

}

return null;

}

parseBeanDefinitionAttributes 方法用来解析普通属性,我们已经在上篇文章中分析过了,这里不再赘述,今天主要来看看其他几个方法的解析工作。

2.Description


首先是 description 的解析,直接通过 DomUtils.getChildElementValueByTagName 工具方法从节点中取出 description 属性的值。这个没啥好说的。

小伙伴们在分析源码时,这些工具方法如果你不确定它的功能,或者想验证它的其他用法,可以通过 IDEA 提供的 Evaluate Expression 功能现场调用该方法,进而验证自己想法,就是下图标出来的那个计算器小图标,点击之后,输入你想执行的代码:

3.parseMetaElements


这个方法主要是解析 meta 属性。前面的视频中已经讲了,这个 meta 属性是保存在 BeanDefinition 中的,也是从 BeanDefinition 中获取的,按照这个思路,来看解析代码就很容易懂了:

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {

NodeList nl = ele.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {

Element metaElement = (Element) node;

String key = metaElement.getAttribute(KEY_ATTRIBUTE);

String value = metaElement.getAttribute(VALUE_ATTRIBUTE);

BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);

attribute.setSource(extractSource(metaElement));

attributeAccessor.addMetadataAttribute(attribute);

}

}

}

可以看到,遍历元素,从中提取出 meta 元素的值,并构建出 BeanMetadataAttribute 对象,最后存入 GenericBeanDefinition 对象中。

有小伙伴说不是存入 BeanMetadataAttributeAccessor 中吗?这其实是 GenericBeanDefinition 的父类,BeanMetadataAttributeAccessor 专门用来处理属性的加载和读取,相关介绍可以参考松哥前面的文章:

4.parseLookupOverrideSubElements


这个方法是为了解析出 lookup-method 属性,在前面的视频中松哥已经和大家聊过,lookup-method 可以动态替换运行的方法,按照这个思路,我们来看下这个方法的源码:

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {

NodeList nl = beanEle.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {

Element ele = (Element) node;

String methodName = ele.getAttribute(NAME_ATTRIBUTE);

String beanRef = ele.getAttribute(BEAN_ELEMENT);

LookupOverride override = new LookupOverride(methodName, beanRef);

override.setSource(extractSource(ele));

overrides.addOverride(override);

}

}

}

可以看到,在这里遍历元素,从 lookup-method 属性中,取出来 methodName 和 beanRef 属性,构造出 LookupOverride 然后存入 GenericBeanDefinition 的 methodOverrides 属性中。

存入 GenericBeanDefinition 的 methodOverrides 属性中之后,我们也可以在代码中查看:

5.parseReplacedMethodSubElements


parseReplacedMethodSubElements 这个方法主要是解析 replace-method 属性的,根据前面视频的讲解,replace-method 可以实现动态替换方法,并且可以在替换时修改方法。

按照这个思路,该方法就很好理解了:

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {

NodeList nl = beanEle.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {

Element replacedMethodEle = (Element) node;

String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);

String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);

ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);

// Look for arg-type match elements.

List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);

for (Element argTypeEle : argTypeEles) {

String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);

match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));

if (StringUtils.hasText(match)) {

replaceOverride.addTypeIdentifier(match);

}

}

replaceOverride.setSource(extractSource(replacedMethodEle));

overrides.addOverride(replaceOverride);

}

}

}

name 获取到的是要替换的旧方法,callback 则是获取到的要替换的新方法,接下来再去构造 ReplaceOverride 对象。

另外由于 replace-method 内部还可以再配置参数类型,所以在构造完 ReplaceOverride 对象之后,接下来还要去解析 arg-type。

6.parseConstructorArgElements


parseConstructorArgElements 这个方法主要是用来解析构造方法的。这个大家日常开发中应该接触的很多。如果小伙伴们对于各种各样的构造方法注入还不太熟悉,可以在微信公众号江南一点雨后台回复 spring5,获取松哥之前录制的免费 Spring 入门教程,里边有讲。

我们来看下构造方法的解析:

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {

NodeList nl = beanEle.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {

parseConstructorArgElement((Element) node, bd);

}

}

}

public void parseConstructorArgElement(Element ele, BeanDefinition bd) {

String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);

String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);

String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

if (StringUtils.hasLength(indexAttr)) {

try {

int index = Integer.parseInt(indexAttr);

if (index < 0) {

error(“‘index’ cannot be lower than 0”, ele);

}

else {

try {

this.parseState.push(new ConstructorArgumentEntry(index));

Object value = parsePropertyValue(ele, bd, null);

ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);

if (StringUtils.hasLength(typeAttr)) {

valueHolder.setType(typeAttr);

}

if (StringUtils.hasLength(nameAttr)) {

valueHolder.setName(nameAttr);

}

valueHolder.setSource(extractSource(ele));

if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {

error("Ambiguous constructor-arg entries for index " + index, ele);

}

else {

bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);

}

}

finally {

this.parseState.pop();

}

}

}

catch (NumberFormatException ex) {

error(“Attribute ‘index’ of tag ‘constructor-arg’ must be an integer”, ele);

}

}

else {

try {

this.parseState.push(new ConstructorArgumentEntry());

Object value = parsePropertyValue(ele, bd, null);

ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);

if (StringUtils.hasLength(typeAttr)) {

最后

分享一些系统的面试题,大家可以拿去刷一刷,准备面试涨薪。

这些面试题相对应的技术点:

  • JVM
  • MySQL
  • Mybatis
  • MongoDB
  • Redis
  • Spring
  • Spring boot
  • Spring cloud
  • Kafka
  • RabbitMQ
  • Nginx

大类就是:

  • Java基础
  • 数据结构与算法
  • 并发编程
  • 数据库
  • 设计模式
  • 微服务
  • 消息中间件

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
息中间件

[外链图片转存中…(img-UjV9m0HG-1714752904577)]

[外链图片转存中…(img-a88K3wXo-1714752904577)]

[外链图片转存中…(img-bXB6k4H3-1714752904578)]

[外链图片转存中…(img-Mb7JQnQg-1714752904578)]

[外链图片转存中…(img-5pH9WeH6-1714752904578)]

[外链图片转存中…(img-GIecXfyf-1714752904578)]

[外链图片转存中…(img-JScwPE0D-1714752904579)]

[外链图片转存中…(img-VOCJH4hu-1714752904579)]

[外链图片转存中…(img-REeHmaJ1-1714752904579)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值