Spring(八):注册Bean(2)

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


spring解析Bean主要分为三步

  • 创建普通的BeanDefinition(解析默认的属性、XML标签)

  • 修饰创建出来的BeanDefinition(解析自定义的属性和标签)

  • 注册BeanDefinition

下面就来看一下如何进行注册的!对应代码就是下面这一行,

在这里插入图片描述

注册BeanDefinition


可以看到,解析和修饰BeanDefinition都是交由delegate去做的,也就是BeanDefinitionParserDelegate,而注册Bean则是由BeanDefinitionReaderUtils去做的,但其实本质上是由BeanDefinitionRegistry去做的,也就是getReaderContext().getRegistry()

其实从某种意义上,修饰也是解析,只不过解析的是自定义属性和标签,所以可以归纳为

  • BeanDefinitionParserDelegate:用于创建、解析Bean

  • BeanDefinitionReaderUtils:用于注册Bean

具体的实现代码如下

public static void registerBeanDefinition(

BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

throws BeanDefinitionStoreException {

// Register bean definition under primary name.

// 从BeanDefinitionHolder去获取Bean的名字

String beanName = definitionHolder.getBeanName();

//根据Bean的名字与BeanDefinitionHolder里的BeanDefinition进行注册

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.

//获取Bean的别名(之前解析过别名,即alias属性)

String[] aliases = definitionHolder.getAliases();

if (aliases != null) {

//遍历所有别名

for (String alias : aliases) {

//注册别名,注意别名映射的是bean的名字

registry.registerAlias(beanName, alias);

}

}

}

可以看到,注册Bean其实就是其名字的注册以及别名的注册(而且本质上是由BeanDefinitionRegistry去做的,BeanDefinitionReaderUtils本质上只是起了个组合作用而已)

  • 获取Bean的名字,根据Bean的名字与BeanDefinition(即Bean的内容)注册

  • 获取Bean的别名,根据Bean的别名与Bean的名字进行注册,这里之所以不用BeanDefinition进行注册,是因为前面已经根据Bean的名字注册过了,所以仅仅需要Bean的名字就能够找到BeanDefinition了

根据Bean的名字进行注册

在这里插入图片描述

可以看到,对于BeanDefinitionRegistry,总共有三个实现类

  • SimpleBeanDefinitionRegistry

  • DefaultListableBeanFactory

  • GenericApplicationContext(默认实现类)

而默认的实现类是GenericApplicationContext,可以看到,现在注册Bean的工作已经交由BeanFactory去做了,所以BeanFactory的工作其实就是用来注册Bean,与IOC容器进行交互的!

并且GenericApplicationContext调用beanFactory进行实现Bean注册

在这里插入图片描述

并且BeanFactory的实现类为DefaultListableBeanFactory(相当于GenericApplicationContext装饰了DefaultListableBeanFactory)

在这里插入图片描述

下面就来看看代码是怎么实现的

@Override

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

throws BeanDefinitionStoreException {

//进行校验

//判断BeanName和BeanDefinition是否为空,只要其中一个为空就结束了

Assert.hasText(beanName, “Bean name must not be empty”);

Assert.notNull(beanDefinition, “BeanDefinition must not be null”);

//对Bean进行校验

if (beanDefinition instanceof AbstractBeanDefinition) {

try {

//对Bean进行校验

((AbstractBeanDefinition) beanDefinition).validate();

}

catch (BeanDefinitionValidationException ex) {

throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,

“Validation of bean definition failed”, ex);

}

}

//判断Bean是否存在,这就涉及到了底层存储Bean的对象

//为一个名为beanDefinitionMap的map容器

BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);

//如果存在

if (existingDefinition != null) {

//如果存在且在配置文件中配置了Bean不允许被覆盖

if (!isAllowBeanDefinitionOverriding()) {

//抛出异常

throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);

}

//如果可以被覆盖

//且覆盖的Bean的角色权限较大

else if (existingDefinition.getRole() < beanDefinition.getRole()) {

// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE

//进行日志记录

if (logger.isInfoEnabled()) {

logger.info(“Overriding user-defined bean definition for bean '” + beanName +

“’ with a framework-generated bean definition: replacing [” +

existingDefinition + “] with [” + beanDefinition + “]”);

}

}

//如果覆盖的Bean的角色权限比较小或等于

//判断两个Bean是不是一样的

else if (!beanDefinition.equals(existingDefinition)) {

//如果不一样,日志记录

if (logger.isDebugEnabled()) {

logger.debug(“Overriding bean definition for bean '” + beanName +

“’ with a different definition: replacing [” + existingDefinition +

“] with [” + beanDefinition + “]”);

}

}

//两个Bean是一样的

else {

if (logger.isTraceEnabled()) {

logger.trace(“Overriding bean definition for bean '” + beanName +

“’ with an equivalent definition: replacing [” + existingDefinition +

“] with [” + beanDefinition + “]”);

}

}

//进行覆盖替换,前面的检查都没什么用,只是记录一些信息而已

//规定到底都是要记录的

this.beanDefinitionMap.put(beanName, beanDefinition);

}

else {

//如果容器里面不存在这个Bean

//判断是否容器已经初始化好了

//这里底层是判断一个名为alreadyCreated的set集合是否为空

//这个集合存放的就是已经创建好的Bean的BeanName!!!

//其实就是判断IOC容器是否已经初始化了,即Spring项目已经启动了

if (hasBeanCreationStarted()) {

// Cannot modify startup-time collection elements anymore (for stable iteration)

//如果容器里面有Bean了,就很有可能产生并发问题

//因为IOC容器,beanDefinitionMap是一个全局变量,存在并发访问的情况

//所以在注册Bean的时候,对beanDefinitionMap进行上锁

synchronized (this.beanDefinitionMap) {

//往容器中去添加Bean

//其实注册的本质就是往容器中添加Bean

//透露一下,其实beanDefinitionMap是一个ConcurrentHashMap

//可见Spring对于IOC容器的问题是做的死死的!!!

this.beanDefinitionMap.put(beanName, beanDefinition);

//由于容器已经存在,所以需要更新

//新建一个ArrayList去存储新的beanName

//容量为原来的beanName集合+1

//个人猜测,这是为了避免内存泄漏

//因为要加载的Bean肯定是有限的,但不知道有多少个而已!

//而这里使用ArrayList去存储所有的BeanName,难免会出现数组容量不够的情况

//如果交由ArrayList自己的扩容机制去做,很大可能就会导致数组容量超出

//从而产生内存泄漏!

//因为容器已经在使用了,不能对容器本身进行修改,只能采用替换的策略!!

//得到的优点就是减少了内存泄漏

List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);

//让新的beanName容器添加旧的beanName容器的所有beanName

updatedDefinitions.addAll(this.beanDefinitionNames);

//再将注册的新Bean的beanName加进去

updatedDefinitions.add(beanName);

//让变量指向新的beanName容器

this.beanDefinitionNames = updatedDefinitions;

//更新manualSinletonNames的set集合

//这个集合用来判断beanName是否重复的,重复就代表重复bean了

removeManualSingletonName(beanName);

}

}

else {

// Still in startup registration phase

// 如果是刚启动直接添加即可

this.beanDefinitionMap.put(beanName, beanDefinition);

this.beanDefinitionNames.add(beanName);

//更新manualSinletonNames的set集合

removeManualSingletonName(beanName);

}

this.frozenBeanDefinitionNames = null;

}

if (existingDefinition != null || containsSingleton(beanName)) {

//重置bean的所有缓存

resetBeanDefinition(beanName);

}

else if (isConfigurationFrozen()) {

clearByTypeCache();

}

}

在这里插入图片描述

在这里插入图片描述

同理,对于更新manualSingletonNames也是做了相同的操作,判断是否已经初始化IOC容器了,假如已经启动好了,就不能进行新加,而是采用替换的方式,而这个manualSingletonNames则是记录了所有需要手动注册的Bean,假如注册成功了,就要从这个集合中移除!

在这里插入图片描述

这里还是用jdk1.8的新特性,传了一个Consumer接口(消费者)和一个Predicate接口(判断)

private void updateManualSingletonNames(Consumer<Set> action, Predicate<Set> condition) {

//判断容器是否已经启动好了

if (hasBeanCreationStarted()) {

//启动好了就进行加锁,并且采用替换方式来修改

// Cannot modify startup-time collection elements anymore (for stable iteration)

//锁住的仍然是IOC容器

synchronized (this.beanDefinitionMap) {

//利用传来的predicate接口进行判断

if (condition.test(this.manualSingletonNames)) {

//利用旧的集合创建新的集合

Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);

//进行删除

action.accept(updatedSingletons);

//直接将新的BeanName的set容器进行替换

this.manualSingletonNames = updatedSingletons;

}

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

Java中高级面试高频考点整理

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

最后分享Java进阶学习及面试必备的视频教学

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

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

}

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

[外链图片转存中…(img-zTTRiVYU-1714767519614)]

Java中高级面试高频考点整理

[外链图片转存中…(img-g7owIWR5-1714767519614)]

[外链图片转存中…(img-IoKLkAOO-1714767519615)]

最后分享Java进阶学习及面试必备的视频教学

[外链图片转存中…(img-Ugnvx5Ee-1714767519615)]

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值