答读者问:关于隐式 id 重复的问题

TienChin 视频杀青啦~采用 Spring Boot+Vue3 技术栈,里边会涉及到各种好玩的技术,小伙伴们来和松哥一起做一个完成率超 90% 的项目,戳戳戳这里-->TienChin 项目配套视频来啦

Spring 源码分析视频教程连载中,感兴趣的小伙伴戳这里:Spring源码应该怎么学?

我自己天天跟大伙讲 Spring 源码,我基本都是分析源码来讲。小伙伴们学习了之后,经常会产生许多千奇百怪的想法,这些想法都很不错,往往这些想法还给了我很大的启发,让我发现原来这个问题还可以从这个角度来理解。

今天我们来看一个小伙伴的提问:

首先我得先夸一句,这个问题真的非常好!问题非常详细,有源码有截图有版本号,该说的都说了,问题非常清晰,我一看就知道发生了什么事情,每天在微信上问松哥问题的人不少,能把问题说的这么清楚的人屈指可数。

我跟大家讲一下这个问题的上下文:

Spring 中 beanName 是不能重复的,一般情况下,我们在定义 Bean 的时候,都要为其指定 beanName 属性,如果不指定,则会默认生成 beanName。在 XML 配置中,如果我们不指定 beanName 或者 id,那么默认生成的 beanName 就是类名的完整路径或者是 类名完整路径+#+编号。这个小伙伴就是在学习了上述内容之后,提出来这个问题。

关于 beanName 自动生成逻辑松哥在视频中都已经详细介绍过了,因此这里就简单和大家梳理一下思路,具体可以参考 Spring 源码视频。

问题分析

小伙伴一共提出两个问题,我们分别来看。

问题一

首先定义了一个 User 对象,但是并未指定 beanName,按照松哥之前在 Spring 源码视频中所讲的,此时会自动给这个 bean 生成 id 和别名,别名是类名的完整路径,即 cn.junhaox.entity.User,id 则是类名完整路径+编号,即 cn.junhaox.entity.User#0,即我们可以通过这两个任意一个名称来访问到第一个对象。

第二个 bean 在定义的时候,则指定了 id,而且指定的 id 恰好就是第一个 bean 自动生成的 id。

这个逻辑上显然是冲突了,导致最终访问的时候,通过 cn.junhaox.entity.User#0 或者 cn.junhaox.entity.User 访问到的是第二个 bean 而不是第一个 bean,这就给人一种第一个 bean 似乎注册失败了的感觉。

我们先来分析一下这个问题。

先来说 bean 的注册,当 bean 在注册的时候,首先会去检查当前 beanName 是否重复(具体在 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition) 方法中),但是这个检查主要是检查我们自己手动配置的 beanName 是否存在重复的情况,并不会去检查自动生成的 beanName 是否重复,这就导致了当第二个 bean 在注册的时候,检查 beanName 是否重复的时候,结果发现 beanName 并不重复,因此就导致了 cn.junhaox.entity.User#0 beanName 重新指向了第二个 bean,那毫无疑问,cn.junhaox.entity.User 作为别名,也重新指向了第二个 bean。

这就是第一个问题产生的原因。

问题二

根据前面的分析,小伙伴们已经知道,对于第一个 bean,由于即没有配置 id,又没有配置 beanName,所以第一个 bean 在注册的时候,会自动生成 id cn.junhaox.entity.User#0 并且会自动生成 beanName cn.junhaox.entity.User

现在第二个问题就是把第一个 bean 的别名作为第二个 bean 的 id 了,导致第二个 bean 似乎访问不到了。

松哥先来说结论,这个问题其实目前在最新版的 Spring 中已经不存在了,具体的处理是在 2022 年 2 月 5 号提交的代码中解决了问题,在当年 3 月份发布的 v6.0.0-M3 版本中这块的代码改过来了,我们来看下代码的变化大家就明白了:

大家可以看到,变化发生在 DefaultListableBeanFactory#registerBeanDefinition 方法中,绿色的代码块就是新增的代码。

新增的代码主要是当我们向容器注册一个 BeanDefinition 的时候,首先会去检查这个 beanName 是否是一个别名,如果是,则检查别名是否允许覆盖,如果别名不允许覆盖,那么该抛异常就抛异常,如果别名允许覆盖,则调用 removeAlias 方法移除别名,这个移除相当于剪掉了别名之间的关系,cn.junhaox.entity.User 将不再作为别名指向 cn.junhaox.entity.User#0 了。

因此,对于第二个问题,从 Spring6.0.0-M3 开始,通过 cn.junhaox.entity.User#0 可以访问到第一个 bean,通过 cn.junhaox.entity.User 则可以访问到第二个 bean。

但是,在此版本之前,并未检查当前 beanName 是否是一个别名,而是直接使用该 beanName 进行注册。当我们去查询 bean 的时候,都是根据 beanName 去查找 bean 的,如果是根据类型,最终也会先根据类型找出 beanName,然后再去查找 bean。根据 beanName 去搜索 bean 的时候,会先根据别名链条确定出最终的 beanName,由于 cn.junhaox.entity.Usercn.junhaox.entity.User#0 之间还存在别名关系,因此当我们按照 beanName cn.junhaox.entity.User 去搜索 bean 的时候,系统会找到这是 cn.junhaox.entity.User#0 的别名,进而找出来 cn.junhaox.entity.User#0 所对应的 bean 并返回,这就导致第二个 bean 将来无法被查找到。

好啦,现在这两个问题都搞明白了吧~

以上内容松哥主要是和大家分享思路,技术细节包括涉及到的 Spring 源码细节在之前的 Spring 视频中都讲过,大家可以参考视频。

欢迎大家继续提问!

TienChin 视频杀青啦~采用 Spring Boot+Vue3 技术栈,里边会涉及到各种好玩的技术,小伙伴们来和松哥一起做一个完成率超 90% 的项目,戳戳戳这里-->TienChin 项目配套视频来啦

Spring 源码分析视频教程连载中,感兴趣的小伙伴戳这里:Spring源码应该怎么学?

相关推荐

  • Java手写分布式锁的实现(非常牛逼)
  • 单机幂等性的 6 种实现方案
  • 基于 Letterize.js + Anime.js 实现炫酷文本特效
  • “我又爱上了「将死」的 Ruby on Rails”
  • 曝小米汽车售价超 30 万元;苹果将禁止摇一摇跳转广告;Slint 1.3 发布|极客头条
  • Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!
  • 到日本做程序员:有专科生年薪40万,有人60多岁还在编程,有人培训完找不到工作
  • 阿里云盘、淘宝、闲鱼、钉钉、饿了么、夸克等……集体崩了!
  • 独自一人,怒发AI顶会!
  • Kubernetes API Gateway 1.0 上线,维护者提出了未来规划
  • HTTP/3 为什么正迅速吞噬世界?
  • 从谷歌 20 年的站点可靠性工程(SRE)中学到的 11 个经验教训
  • 收录 800+ 开源项目!InfoQ 开源系列报告带你洞察中国开源世界
  • 微服务不香了?单体化改造为我们节省上万核 CPU!
  • 听我的,日志还是得好好打!
  • JSDoc ,一个可替代 TypeScript 的方案?
  • 面试必问题:有没有比读写锁更快的锁实现?
  • 基于 Vue3.2 + Element-Plus 的后台管理框架 Geeker Admin
  • 终结扩散模型,IGN单步生成逼真图像!UC伯克利谷歌革新LLM,美剧成灵感来源
  • 浙大拿下唯一最佳论文奖,中国团队喜获三项大奖!ACM MultiMedia 2023奖项揭幕