Spring(五)

//解析元数据

parseMetaElements(ele, bd);

//解析lookup-method属性

parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

//解析replaced-method属性

parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

//解析构造函数的参数

parseConstructorArgElements(ele, bd);

//解析property子元素

parsePropertyElements(ele, bd);

//解析qualified子元素

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;

}

整体步骤如下

  • 开启追踪过程

  • 创建beanDefinition实体去承载Bean

  • 硬编码去解析默认bean的各种属性

  • 对一些特殊属性进行二次加工

  • 元数据

  • property子元素

  • qualifier子元素

  • 构造函数的参数

  • lookup-method属性

  • replaced-method属性

  • 关闭追踪

下面对每一步进行说明一下

开启追踪、并获取class和parent属性

在这里插入图片描述

这一步很简单。

  • 将当前正在解析的Bean放在ParseState(底层是一个队列),通过队列去追踪当前解析的Bean。

  • 解析Element去获取parent和class属性

创建用于属性承载的BeanDefinition

在这里插入图片描述

可以看到第一步的代码就是去创建BeanDefinition(BeanDefinitionParserDelegate中的方法),前面提到过,BeanDefinition其实相当于就是Bean,不信可以看一下官方文档

在这里插入图片描述

下面来看一下这个BeanDefinition

在这里插入图片描述

BeanDefinition是一个接口,有4个实现类,其中三个继承了AbstractBeanDefinition

BeanDefinition对应的就是配置文件元素标签在容器中的内部表示形式,说白了就是一个BeanDefinition就是IOC容器里的bean

bean标签里面的class、scope、lazy-init等配置属性,都会与BeanDefinition提供的方法一一对应

在这里插入图片描述

AbstractBeanDefinition

下面来看下这个AbstractBeanDefinition抽象类

在这里插入图片描述

可以看到,继承该抽象类的子类有三个

  • RootBeanDefinition:对应一般的bean元素标签,通俗来说,就是没有父bean的bean

  • ChildBeanDefinition:对应bean元素标签里面的bean元素标签,也就是bean的子标签

  • GenericBeanDefinition:2.5版本才加入的,一站式服务类

整体的流程就是

Spring通过BeanDefinition将配置文件里面的bean转换为容器的内部表示(具体就是RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition,这三个BeanDefinition),然后将这些BeanDefinition注册到BeanDefinitionRegistry。BeanDefinitionRegistry相当于Spring存储配置信息的内存数据库,使用map的形式去保存的,后面的操作就可以直接从BeanDefinitionRegistry中去读取配置信息

createBeanDefinition方法

/**

  • Create a bean definition for the given class name and parent name.

  • @param className the name of the bean class

  • @param parentName the name of the bean’s parent bean

  • @return the newly created bean definition

  • @throws ClassNotFoundException if bean class resolution was attempted but failed

*/

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)

throws ClassNotFoundException {

//可以看到,这里创建bean的工作交由了BeanDefinitionReaderUtils去做了

return BeanDefinitionReaderUtils.createBeanDefinition(

parentName, className, this.readerContext.getBeanClassLoader());

}

BeanDefinitionReaderUtils的源码如下

public static AbstractBeanDefinition createBeanDefinition(

@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

//创建GenericBeanDefiniton

//使用GenericBeanDefinition

GenericBeanDefinition bd = new GenericBeanDefinition();

//注入parent(parent可能为空)

bd.setParentName(parentName);

//如果className不为空

if (className != null) {

//如果className不为空,classLoader也不为空

if (classLoader != null) {

//通过反射去获取bean对应java对象的class对象

//并且注入进bean中

bd.setBeanClass(ClassUtils.forName(className, classLoader));

}

//如果className不为空,但没有给classLoader

else {

//因为没有classLoader

//所以只将className保存进bean中

bd.setBeanClassName(className);

}

}

//返回GenericBeanDefinition

return bd;

}

从代码上可以看到

  • 创建Bean的实际工作是由BeanDefinitionReaderUtils去做的

  • 一开始创建的BeanDefinition类型为GenericBeanDefinition

  • 注入parent与class属性

  • class属性代表的是Java类型,如果有给上classLoader,那就给BeanDefinition注入class对象

  • 如果没有给上classLoader,那就注入class属性的值

  • 这两者都是可以为空的!

解析各种属性

创建了BeanDefinition之后,就有了实例去承载bean标签的配置信息了,可以对bean的各种配置信息进行解析了

parseBeanDefinitionAttributes

首先是经过parseBeanDefinitionAttributes方法解析(Element就是bean在xml的配置信息)

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,

@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

//判断是否有singleton属性

//如果有,就直接error

if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {

//1.x版本的singleton已经被scope取代了!

//之前该标签是用来控制bean是否为单例的!

error(“Old 1.x ‘singleton’ attribute in use - upgrade to ‘scope’ declaration”, ele);

}

//下面两个else if的判断

//其实是判断有没有给bean设置scope属性

//scope其实就是bean的作用域(官方文档说有6种,并且可以自定义)

else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {

//如果设置了scope属性,那就采用配置文件的

bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));

}

//如果配置文件没有配置,那就再判断containingBean是否为空

//针对子bean标签

else if (containingBean != null) {

//这个设置是针对子bean标签的,因为只有bean子标签才会有containning bean

//也就是父标签,所以默认的子bean的scope是父bean一样的!

//这里避免是内部bean,所以使用containning bean的scope来作为默认的参考

// Take default from containing bean in case of an inner bean definition.

bd.setScope(containingBean.getScope());

}

//判断有没有abstract属性

if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {

//有abstract属性就进行,并且还规定abstract属性必须只能为true或者false

//否则会失效

//通过用true字符串去进行equals比较

bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));

}

//解析lazyInit属性

String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);

//判断lazyInit属性是不是为default

//为空,或者值为default,返回true

if (isDefaultValue(lazyInit)) {

//为空,或者lazyInit属性为default,那就设置默认值

//默认值由DocumentDefaultsDefinition决定

lazyInit = this.defaults.getLazyInit();

}

//注入lazyInit属性进bean

bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

//获取autowire属性

String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);

//注入autowire属性,总共有4种,其中一种弃用

//1.byName,2.byType,3.construct,4.autodetect

bd.setAutowireMode(getAutowireMode(autowire));

//判断有没有depends-on属性

if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {

String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);

bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));

}

//解析autowire-candidate属性

String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);

//如果没有设置,或者为default

if (isDefaultValue(autowireCandidate)) {

//那就使用默认值

String candidatePattern = this.defaults.getAutowireCandidates();

//autowire-candidate属性可以设置多个,使用逗号间隔

//只有默认值,才会有多个的情况!

if (candidatePattern != null) {

//所以如果有设计的话,需要解析并且使用字符串数组来接收

String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);

bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));

}

}

else {

//如果不是默认值,只可能是true或者false

//同理使用true字符串去进行equals比较

bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));

}

//解析primary属性

if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {

//同理为true或者为false

//不为空就进行注入

bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));

}

//下面是生命周期的配置

//解析init-method属性

if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {

//不为空就进行注入

String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);

bd.setInitMethodName(initMethodName);

}

//如果init-method属性没有设置(创建的时候执行的方法)

//那就去判断有没有默认值

else if (this.defaults.getInitMethod() != null) {

//没有设置且有默认值,init-method属性就设为默认值

bd.setInitMethodName(this.defaults.getInitMethod());

//并且init-method的方法设置为不强制执行

//因为是默认的,所以不强制执行

bd.setEnforceInitMethod(false);

}

//解析destory-method属性

//bean销毁时执行的方法

//与method-init属性一样,有的注入,没有就判断默认的有没有

//没有设置且有默认的,是不会强制执行

if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {

String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);

bd.setDestroyMethodName(destroyMethodName);

}

else if (this.defaults.getDestroyMethod() != null) {

bd.setDestroyMethodName(this.defaults.getDestroyMethod());

bd.setEnforceDestroyMethod(false);

}

//解析factory-method属性

if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {

bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));

}

//解析factory-bean属性

if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {

bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));

}

return bd;

}

总结一下这个方法

  1. 创建BeanDefinition,为GenericBeanDefinition

  2. 对bean的所有属性进行解析

至此,该bean标签基本上已经所有属性已经解析完成了,剩下的就是里面的子标签了

scope作用域与init-method、destory-method生命周期

官方文档截图

在这里插入图片描述

在这里插入图片描述

parseMetaElements

parseBeanDefinitionAttributes已经帮我们将bean的属性都解析完了,接下来就是子标签的解析了

子标签的解析,首先是对元数据meta的解析

可能很少人用过meta子标签,因为meta标签的作用其实就是做一个额外的声明信息,声明了之后,该bean对应的BeanDefinition可以通过getAttribute方法去获取里面的信息

在这里插入图片描述

下面是源码

在这里插入图片描述

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {

//meta是子标签

//所以,先要获取当前标签的所有子标签

NodeList nl = ele.getChildNodes();

//遍历子标签

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

Node node = nl.item(i);

//提取出meta子标签出来

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

Element metaElement = (Element) node;

//取meta子标签的key和value属性

String key = metaElement.getAttribute(KEY_ATTRIBUTE);

String value = metaElement.getAttribute(VALUE_ATTRIBUTE);

//meta子标签对应的对象为BeanMetadataAttribute

BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);

attribute.setSource(extractSource(metaElement));

//存进BeanDefinition中(因为放进来的参数为BeanDefinition)

//底层为一个LinkedHashMap

//底层是交由AttributeAccessorSupport去实现的

attributeAccessor.addMetadataAttribute(attribute);

}

}

}

在这里插入图片描述

@Override

public void setAttribute(String name, @Nullable Object value) {

Assert.notNull(name, “Name must not be null”);

//如果value为null

//代表删除动作

//不为null,才是插入,插入进底层的LinkedHashMap中

if (value != null) {

this.attributes.put(name, value);

}

else {

removeAttribute(name);

}

}

在这里插入图片描述

parseLookupOverrideSubElements

解析来解析的是lookup-method子标签

这个子标签跟meta一样,我们都很少使用,但在某些场景,这个标签是非常有用的,这个标签通常称为获取器注入

它的作用是,可以去直接获取其他bean作为方法的返回值,即父bean把一个方法声明为返回某一个类型的bean,我们可以通过lookup-method的子标签,从spring容器中去取出这个bean,然后给父bean的方法进行返回,总的来说,就是替换了返回bean

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {

//原理与meta解析几乎一致

//获取子标签

NodeList nl = beanEle.getChildNodes();

//遍历子标签

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

Node node = nl.item(i);

//判断是否是lookup-method标签

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

Element ele = (Element) node;

//获取lookup-method标签的name和bean属性

//bean对应的是容器中的另外一个bean的id属性

//name属性对应的是父bean的方法名!

String methodName = ele.getAttribute(NAME_ATTRIBUTE);

String beanRef = ele.getAttribute(BEAN_ELEMENT);

//lookup-method标签对应的java对象为LookUpOverride

LookupOverride override = new LookupOverride(methodName, beanRef);

override.setSource(extractSource(ele));

//同理放入BeanDefinition中

//这里BeanDefinition存储的底层是一个CopyOnWriteArraySet

overrides.addOverride(override);

}

}

}

在这里插入图片描述

parseReplacedMethodSubElements

接下来是解析replaced-method子标签

这个标签也很少有,也还是说一下它的用途,replaced-method标签可以取替换返回实体bean,还可以动态地更改原有方法的逻辑,总的来说就是可以将父bean的方法整体给替换掉,也就是执行的方法已经是另外一个bean的方法了,但这个方法并不是可以随便写的,必须另外一个bean实现MethodReplacer接口,重写的那个方法才可以进行替换

下面是解析的源码

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {

//前面的逻辑与前面两个属性解析都差不多一致,就不再赘述
先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!

img

总结

虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。
系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-gayw54wN-1711460241084)]

[外链图片转存中…(img-cvbpmWCC-1711460241084)]

[外链图片转存中…(img-r5izxzqi-1711460241085)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!

img

总结

虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。

[外链图片转存中…(img-XNDElARH-1711460241085)]

[外链图片转存中…(img-koRj20Xf-1711460241086)]

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。
需要更多Java资料的小伙伴可以帮忙点赞+关注,点击传送门,即可免费领取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值