spring源码篇——BeanDefinition的注册
spring-framework 版本:v5.3.19
文章目录
- 注解方式(AnnotationConfigApplicationContext)
- AnnotationConfigApplicationContext#register
- AnnotatedBeanDefinitionReader#doRegisterBean
- BeanDefinitionRegistry#registerBeanDefinition
- AnnotationConfigApplicationContext#scan
- ClassPathBeanDefinitionScanner#doScan
- findCandidateComponents
- BeanDefinitionRegistry#registerBeanDefinition
- XML方式(AbstractXmlApplicationContext)
- AbstractXmlApplicationContext#loadBeanDefinitions
- XmlBeanDefinitionReader#doLoadBeanDefinitions
- BeanDefinitionDocumentReader#doRegisterBeanDefinitions
- BeanDefinitionRegistry#registerBeanDefinition
- 总结
注解方式(AnnotationConfigApplicationContext)
AnnotationConfigRegistry接口有两个方法,分别对应了注解生成beanDefinition的两种方式
来到其实现类AnnotationConfigApplicationContext可以发现类里有两个成员变量reader和scanner
reader对应register方法(根据传入class生成beanDefinition)
scanner对应scan方法(根据传入包名扫描特定注解表示的class生成beanDefinition)
先看register方法的实现
AnnotationConfigApplicationContext#register
AnnotatedBeanDefinitionReader#doRegisterBean
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,@Nullable BeanDefinitionCustomizer[] customizers) {//根据bean类生成beanDefinition(实际上就是解析出 AnnotationMetadata 元数据)AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);//根据condition注解条件判断是否跳过if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}//创建bean之后的回调方法abd.setInstanceSupplier(supplier);//设置作用域ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());//得到beanNameString beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));//通用注解解析:Lazy、Primary、DependsOn、Role、DescriptionAnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (qualifiers != null) {for (Class<? extends Annotation> qualifier : qualifiers) {if (Primary.class == qualifier) {abd.setPrimary(true);}else if (Lazy.class == qualifier) {abd.setLazyInit(true);}else {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}//自定义处理if (customizers != null) {for (BeanDefinitionCustomizer customizer : customizers) {customizer.customize(abd);}}//封装成BeanDefinitionHolderBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//是否需要代理definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//注册beanDefinition到registryBeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}
BeanDefinitionRegistry#registerBeanDefinition
进入BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry)方法
可以发现最终会调用BeanDefinitionRegistry.registerBeanDefinition方法。但这是一个接口,其对应的实现类是哪个呢?
其实就是AnnotationConfigApplicationContext本身。
在赋值reader的时候会把自身传进去赋值给register,而AnnotationConfigApplicationContext继承自GenericApplicationContext,GenericApplicationContext又继承自BeanDefinitionRegistry,所以AnnotationConfigApplicationContext也是一个BeanDefinitionRegistry。
GenericApplicationContext.registerBeanDefinition实际上只是调用了另一个实现类DefaultListableBeanFactory的registerBeanDefinition方法
reader赋值
GenericApplicationContext.registerBeanDefinition
综上最终会来到DefaultListableBeanFactory#registerBeanDefinition,也即BeanDefinitionRegistry#registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");//抽象beanDefinition的一些处理(beanDefinition也有继承关系)if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}//beanName是否已经存在beanDefinitionBeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {//不允许覆盖则报错if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}//打印一些日志else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}//覆盖原beanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);}else {//判断是否有bean开始注册了,有就加锁保证线程安全if (hasBeanCreationStarted()) {synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}//没有就直接注册beanDefinitionelse {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}//如果存在过beanName对应的beanDefinition或者单例bean则重置beanDefinition//移除合并的beanDefinition、销毁单例bean、执行MergedBeanDefinitionPostProcessor.resetBeanDefinitionif (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}//如果beanDefinition被冻结的一些处理else if (isConfigurationFrozen()) {clearByTypeCache();}}
至此beanDefinition就注册到容器上了。
接下来扫描包的方式即scan方法
AnnotationConfigApplicationContext#scan
ClassPathBeanDefinitionScanner#doScan
![ protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {//扫描生成BeanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {//得到作用域元数据ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);//设置作用域candidate.setScope(scopeMetadata.getScopeName());//得到beanNameString beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}//通用注解解析:Lazy、Primary、DependsOn、Role、Descriptionif (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//检测BeanDefinition的beanName是否与其他BeanDefinition存在冲突//虽然是一个判断,当时会有三种情况:true:beanName没有冲突,false:有冲突但兼容,抛异常:有冲突且不兼容if (checkCandidate(beanName, candidate)) {//没有冲突则注册BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//注册beanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}]
findCandidateComponents
findCandidateComponents方法返回Set < BeanDefinition > 。可以发现当有指定spring.components文件时,会直接用该文件加快加载,否则扫描包下所有资源。
scanCandidateComponents
这里会先得到包下所有的Resource,再依次获取元数据封装到MetadataReader,当满足excludeFilter/includeFilter/condition条件时,创建ScannedGenericBeanDefinition。
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}try {//得到元数据MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//判断excludeFilter/includeFilter/condition条件if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);//判断excludeFilter/includeFilter/condition条件if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}//添加扫描beanDefinitioncandidates.add(sbd);}//以下都是日志异常处理......
isCandidateComponent
BeanDefinitionRegistry#registerBeanDefinition
进入registerBeanDefinition(definitionHolder, this.registry);
可以发现最后跟register一样调用BeanDefinitionReaderUtils#registerBeanDefinition进而调用BeanDefinitionRegistry#registerBeanDefinition,这里就不再说一遍了。
XML方式(AbstractXmlApplicationContext)
AbstractXmlApplicationContext#loadBeanDefinitions
AbstractXmlApplicationContext会调用BeanDefinitionReader#loadBeanDefinitions加载xml文件,而xml方式具体的实现类就是XmlBeanDefinitionReader。
虽然XmlBeanDefinitionReader和上面提到的AnnotatedBeanDefinitionReader都是以BeanDefinitionReader结尾,但是实际上这两个从类继承来看没有什么关系,并不来自于同一基类也不互相继承。XmlBeanDefinitionReader实现了BeanDefinitionReader接口,而AnnotatedBeanDefinitionReader不实现任何接口也不继承任何类。
跟踪XmlBeanDefinitionReader代码来到真正做事的地方:doLoadBeanDefinitions方法
XmlBeanDefinitionReader#doLoadBeanDefinitions
最终来到BeanDefinitionDocumentReader.doRegisterBeanDefinitions()方法
BeanDefinitionDocumentReader#doRegisterBeanDefinitions
这一步主要就是对各种标签的解析
BeanDefinitionRegistry#registerBeanDefinition
进入bean标签的解析
可以发现最后同样调用BeanDefinitionReaderUtils#registerBeanDefinition进而调用BeanDefinitionRegistry#registerBeanDefinition,剩下的步骤就跟注解方式的一样了。
总结
注解方式可register直接注册,也可scan扫描注册。register方式通过AnnotatedBeanDefinitionReader创建beanDefinition(同时会得到元数据),再根据元数据等设置完beanDefinition属性后,用BeanDefinitionRegistry注册到容器。scan方式通过ClassPathBeanDefinitionScanner先扫描生成所有的BeanDefinition(同时会得到元数据),依次遍历BeanDefinition,根据元数据等设置BeanDefinition的属性,最后检查是否冲突,当无冲突时也用BeanDefinitionRegistry注册到容器。
xml方式通过XmlBeanDefinitionReader先加载xml文件并封装成document,再将document委托给BeanDefinitionDocumentReader对各种标签进行解析生成beanDefinition,最后用BeanDefinitionRegistry注册到容器中。
可以发现无论是注解register还是注解scan还是xml最终在注册到容器时都会用到BeanDefinitionRegistry,而其具体的实现就是DefaultListableBeanFactory#registerBeanDefinition。
相关文章:

spring源码篇——BeanDefinition的注册
spring-framework 版本:v5.3.19 文章目录注解方式(AnnotationConfigApplicationContext)AnnotationConfigApplicationContext#registerAnnotatedBeanDefinitionReader#doRegisterBeanBeanDefinitionRegistry#registerBeanDefinitionAnnotatio…...

virtualbox7虚拟机中安装苹果macOS big sur系统详细教程
第1步,在 Windows 10/11 PC 上启用虚拟化。 现在的电脑一般都默认开启虚拟化技术了。 如果你遇到一些报错,比如收到错误消息“无法在虚拟机上打开会话”,可以查看 如果没有遇到问题,可以直接进入到第二步。 第2步,在…...

用spectralayers 简单去一下人声做个伴奏
最近有个同事说有个工作要一个歌的伴奏不会下载问我能不能给下一个。问题是我五音不全,也不咋关注伴奏这方面的事儿,然后巧了,当天晚上就有个网上的大哥在群里聊天的时候说有个去人声比较给力的软件,我马上给要来了。 软件叫啥sp…...
高峰对话|深度探讨「多云与边缘」
2022 年 12 月,分析师 Zeus Kerravala 与 VMware 通信运营商和边缘事业部高级副总裁兼总经理 Sanjay Uppal 进行非常有启发性的谈话,分享了科技行业领导者的见解。 二位主要围绕以下主题进行探讨: 📍 如何定义多云,以…...
开发手册——一、编程规约_2.常量定义
这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中。 反例:String key "Id#…...

Sandstorm 建设者亮点——2023 年 2 月
隆重推出 Sandstorm 建设者亮点——2023 年 2 月版,这是由最厉害的 Sandstorm 社区制作的独一无二的 NFT 系列。 从突破性的兔子机器人到神奇的蒸汽朋克海盗船,Sandstorm 建设者亮点 NFT 系列展示了一系列独一无二的创作。 19 项新资产将添加至 Sandstor…...

MyBatis快速入门
创建表(自行完成)创建模块,引入坐标(1).进入mybatis官网:MyBatis中文网按步骤进行添加坐标先添加mybatis依赖然后手动添加mysql驱动junit单元测试坐标:logback坐标:用的时候直接复制…...
Mysql的一些提权方式(mysql提权、UDF)
目录 bash命令提权 必要条件 实验 UDF提权 什么是UDF 必要条件 实验 手动测试...
【2023】DevOps、SRE、运维开发面试宝典之Docker相关面试题
文章目录 1、docker的工作原理是什么2、docker的组成包含哪几大部分3、讲一下镜像的分层结构以及为什么要使用镜像的分层结构?4、简单描述一下Dockerfile的整个构建镜像过程?5、Docker的四种网络类型?6、Docker跨宿主机通讯的方式1、docker的工作原理是什么 docker是一个Cl…...

圣杯布局的实现方式
1.什么是圣杯布局? 左右盒子固定,中间盒子自适应 2.实现方式 (1)flex布局 思路:左右盒子给固定的宽高,中间盒子flex:1 <!DOCTYPE html> <html lang"en"> <head> <met…...
RecastDemo用法
这里写自定义目录标题recastnavigation介绍recastnavigation的内容RecastDemo安装RecastDemo介绍可配置参数合理的创建标题,有助于目录的生成如何改变文本的样式生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个…...

IIC总线式驱动开发(mpu6050)(二)
目录 六、I2C总线二级外设驱动开发方法 七、I2C总线二级外设驱动开发之名称匹配 1. i2c_register_board_info 2. i2c_new_device:明确二级外设地址的情况下可用 3. i2c_new_probed_device 八、I2C总线二级外设驱动开发之设备树匹配 六、I2C总线二级外设驱动开…...

盘点一下那些远程办公的神仙公司
其实远程办公已经有50多年的历史了,这几年,这种工作方式越来越受到大家的喜欢,对于员工来说,工作效率可以大幅提高,节省下来的通勤时间和成本,有更多的时间花在工作上。可以更好的平衡工作与生活。对于公司…...

Spring Cloud Alibaba全家桶(四)——微服务调用组件Feign
前言 本文小新为大家带来 微服务调用组件Feign 的相关知识,具体内容包含什么是Feign,Spring Cloud Alibaba快速整合OpenFeign,Spring Cloud Feign的自定义配置及使用(包括:日志配置、契约配置、自定义拦截器实现认证逻…...

安装pytorch
一、在anaconda中创建虚拟环境 打开Anaconda Prompt创建一个虚拟环境。比如要创建一个名字为pytorch的虚拟环境,可以如下输入。其中python3.7指定该虚拟环境的python版本号。 conda create -n pytorch python3.7 二、进入新创建的虚拟环境。 创建好虚拟环境后&a…...

自动化测试 Appium之Python运行环境搭建 Part2
环境部署 1、安装Android SDK 安装好后,配置ANDROID_HOME环境变量,设置为Android SDK安装路径(例中:D:\Program Files (x86)\Android\android-sdk) 2、安装其它SDK相关软件包 安装好Android SDK后,选择打开Android SDK Manager…...

LeetCode 2 - 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个数都不会以 0 开…...

用Python实现九九乘法表的几种方式,最简单只需一行代码
前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 我们在学习Python的过程中需要不断的积累和练习,这样才能够走的更远, 今天一起来学习怎么用Python写九九乘法表~ 更多教程源码资料电子书: 点击此处跳转文末名片获取 第一种方法、for-for 代码&…...

入职外包三个月,我提桶跑路了
有一种打工人的羡慕,叫做“大厂”。 真是年少不知大厂香,错把青春插稻秧。 但是,在深圳有一群比大厂员工更庞大的群体,他们顶着大厂的“名”,做着大厂的工作,还可以享受大厂的伙食,却没有大厂…...

企业邮箱对企业有哪些好处以及便捷性
企业邮箱拥有更专业的办公功能,更适合职场使用。同时,使用企业邮箱还可以帮助企业“公私分明”。一方面保护了公司信息,另一方面也提高了工作效率。加上公司统一邮箱也有助于提升公司形象。使用企业邮箱除了收发邮件方便外,还可以…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...