《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
上篇文章我们最终分析到下面这个方法:
@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 方法用来解析普通属性,我们已经在上篇文章中分析过了,这里不再赘述,今天主要来看看其他几个方法的解析工作。
首先是 description 的解析,直接通过 DomUtils.getChildElementValueByTagName 工具方法从节点中取出 description 属性的值。这个没啥好说的。
小伙伴们在分析源码时,这些工具方法如果你不确定它的功能,或者想验证它的其他用法,可以通过 IDEA 提供的 Evaluate Expression 功能现场调用该方法,进而验证自己想法,就是下图标出来的那个计算器小图标,点击之后,输入你想执行的代码:
这个方法主要是解析 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。
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面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!