subElement = (Element) node;
}
}
}
//获取construct-arg的ref和value属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//假如两个同时拥有,或者两个属性拥有一个,但解析出的子标签不为null,证明有子标签
//就代表出现了重复给构造参数了
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
//重复给构造参数,报错处理
error(elementName +
" is only allowed to contain either ‘ref’ attribute OR ‘value’ attribute OR sub-element", ele);
}
//如果并没有重复,假如是ref属性
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty ‘ref’ attribute", ele);
}
//ref属性用RuntimeBeanReference对象存储
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//标明来源
ref.setSource(extractSource(ele));
//返回
return ref;
}
//如果是value属性
else if (hasValueAttribute) {
//使用TypedStringValue进行存储
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
//返回
return valueHolder;
}
//如果是子标签形式
else if (subElement != null) {
//交由parsePropertySubElement继续处理子标签
//因为子标签也是有多种形式的
return parsePropertySubElement(subElement, bd);
}
//既没有value属性,又没有ref属性,又没有子标签
else {
// Neither child element nor “ref” or “value” attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
这里就不再赘述了
PropertyValue是怎样被记录的?
前面不是说了,对于property标签,是会被放进去PropertyValues对象里面的(与Construct-arg标签被存放进ConstructArgumentValues对象一样)
BeanDefinition这个接口只有一个实现类就是AbstractBeanDefinition,也就是在AbstractBeanDefinition中,就初始化了这个存储容器了(与ConstructorArgumentValues一样),可知AbstractBeanDefinition有着记录创建Bean的细节
可以见到,其本质是MutablePropertyValues对象
而且其底层的容器有两个
-
PropertyValueList:一个ArrayList集合,并且默认容量为0,这个ArrayList就是用来存储PropertyValue的
-
proccessedProperties:一个String的集合,这个就是用来去重的!也就是原先判断这个property是否正在注册(通过name属性判断,),里面存储的就是name属性,本质上是一个HashSet并且初始化的容量为4
不过,决定这个property是否可以存放进来,是由这两个容器共同决定的
从代码上可以看到,判断这个property是否已经加载过,需要满足2个条件
-
遍历PropertyValueList,里面没有一个propertyValue的name是与其一样的
-
且proccessedProperties里面为空或者proccessedProperties没有这个propertyName记录
满足上面两个条件,才会返回false,代表这个property没有被加载过
下面再看看添加propertyValue进propertyValueList的逻辑
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
//遍历存储propertyValue的propertyValueList
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue currentPv = this.propertyValueList.get(i);
//如果出现了重复PropertyName
if (currentPv.getName().equals(pv.getName())) {
//判断是否需要合并处理
pv = mergeIfRequired(pv, currentPv);
//合并处理后重新修改这个propertyValue
setPropertyValueAt(pv, i);
return this;
}
}
//没有出现重复的propertyName,直接往propertyValueList里面添加
this.propertyValueList.add(pv);
return this;
}
可以看到,spring的逻辑真的严谨,前面已经判断过一次了,这里又要进行判断(一般来说,应该不会走去合并处理的)
合并的逻辑
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
Object value = newPv.getValue();
//如果value实现了Mergeable接口
//那就进行合并!
if (value instanceof Mergeable) {
Mergeable mergeable = (Mergeable) value;
if (mergeable.isMergeEnabled()) {
//value进行合并后,产生新的propertyValue并返回
Object merged = mergeable.merge(currentPv.getValue());
return new PropertyValue(newPv.getName(), merged);
}
}
//没实现Mergeable接口,直接返回旧值
return newPv;
}
终于到最后一个qualifier标签了
对于qualifier我们通常都是使用注解@Qualifier的,那么这个qualifier标签有什么用呢?
要知道,在使用Spring框架中进行自动注入的时候,Spring容器中提供的候选Bean必须有而且仅仅只能有一个,当找不到匹配的Bean时,Spring容器将抛出BeanCreationException
qualifier标签就是用来定义这个bean的别名的,代表这个bean必须根据名称(ByName)才会被选为候选bean(一般时ByType),即根据bean的名称进行注入!
/**
- Parse qualifier sub-elements of the given bean element.
*/
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
//同理,遍历所有子标签
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遇到qualifier标签就执行方法,parseQualifierElement
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
parseQualifierElement((Element) node, bd);
}
}
}
parseQualifierElement方法
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
//判断type属性是否为空
//type属性是
if (!StringUtils.hasLength(typeName)) {
error(“Tag ‘qualifier’ must have a ‘type’ attribute”, ele);
return;
}
//开始跟踪
this.parseState.push(new QualifierEntry(typeName));
try {
//使用AutowireCandidateQualifier去存储qualifier标签
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
String value = ele.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
NodeList nl = ele.getChildNodes();
//对Qualifier子标签进行解析!
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element) node;
//必须要有key属性和value属性
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
}
else {
error(“Qualifier ‘attribute’ tag must have a ‘name’ and ‘value’”, attributeEle);
return;
}
}
}
//当前bean去存储qualiier
bd.addQualifier(qualifier);
}
finally {
this.parseState.pop();
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
写在最后
学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!
最后再分享的一些BATJ等大厂20、21年的面试题,把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
Mybatis面试专题
MySQL面试专题
并发编程面试专题
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!
最后再分享的一些BATJ等大厂20、21年的面试题,把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
[外链图片转存中…(img-kFrlQI86-1712675881804)]
Mybatis面试专题
[外链图片转存中…(img-ALrr9vn9-1712675881804)]
MySQL面试专题
[外链图片转存中…(img-4Qw2R6yt-1712675881804)]
并发编程面试专题
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-d9c6SinW-1712675881805)]