Spring Bean required a single bean, but 2 were found,发现多个 Bean
问题复现
- 在使用 @Autowired 时,不管你是菜鸟级还是专家级的 Spring 使用者,都应该制造或者遭遇过类似的错误:
required a single bean, but 2 were found
- 顾名思义,我们仅需要一个 Bean,但实际却提供了 2 个(这里的“2”在实际错误中可能是其它大于 1 的任何数字)。
- 为了重现这个错误,我们可以先写一个案例来模拟下。假设我们在开发一个学籍管理系统案例,需要提供一个 API 根据学生的学号(ID)来移除学生,学生的信息维护肯定需要一个数据库来支撑,所以大体上可以实现如下:
@RestController @Slf4j @Validated public class StudentController {@AutowiredDataService dataService;@RequestMapping(path = "students/{id}", method = RequestMethod.DELETE)public void deleteStudent(@PathVariable("id") @Range(min = 1,max = 100) int id){dataService.deleteStudent(id);}; }
- 其中 DataService 是一个接口,其实现依托于 Oracle,代码示意如下:
public interface DataService {void deleteStudent(int id); }@Repository @Slf4j public class OracleDataService implements DataService{@Overridepublic void deleteStudent(int id) {log.info("delete student info maintained by oracle");} }
- 截止目前,运行并测试程序是毫无问题的。但是需求往往是源源不断的,某天我们可能接到节约成本的需求,希望把一些部分非核心的业务从 Oracle 迁移到社区版 Cassandra,所以我们自然会先添加上一个新的 DataService 实现,代码如下:
@Repository @Slf4j public class CassandraDataService implements DataService{@Overridepublic void deleteStudent(int id) {log.info("delete student info maintained by cassandra");} }
- 实际上,当我们完成支持多个数据库的准备工作时,程序就已经无法启动了,报错如下:
- 很显然,上述报错信息正是我们这一小节讨论的错误,那么这个错误到底是怎么产生的呢?接下来我们具体分析下。
案例分析
- 要找到这个问题的根源,我们就需要对 @Autowired 实现的依赖注入的原理有一定的了解。首先,我们先来了解下 @Autowired 发生的位置和核心过程。
- 当一个 Bean 被构建时,核心包括两个基本步骤:
- 执行
AbstractAutowireCapableBeanFactory#createBeanInstance
方法:通过构造器反射构造出这个 Bean,在此案例中相当于构建出 StudentController 的实例; - 执行
AbstractAutowireCapableBeanFactory#populate
方法:填充(即设置)这个 Bean,在本案例中,相当于设置 StudentController 实例中被 @Autowired 标记的 dataService 属性成员。
- 执行
- 在步骤 2 中,“填充”过程的关键就是执行各种 BeanPostProcessor 处理器,关键代码如下:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {//省略非关键代码for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);//省略非关键代码}}} }
- 在上述代码执行过程中,因为 StudentController 含有标记为 Autowired 的成员属性 dataService,所以会使用到
AutowiredAnnotationBeanPostProcessor
(BeanPostProcessor 中的一种)来完成“装配”过程:找出合适的 DataService 的 bean 并设置给StudentController#dataService
。如果深究这个装配过程,又可以细分为两个步骤:- 寻找出所有需要依赖注入的字段和方法,参考
AutowiredAnnotationBeanPostProcessor#postProcessProperties
中的代码行:InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
- 根据依赖信息寻找出依赖并完成注入,以字段注入为例,参考 AutowiredFieldElement#inject 方法:
@Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;//省略非关键代码try {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);//寻找“依赖”,desc为"dataService"的DependencyDescriptorvalue = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}}//省略非关键代码if (value != null) {ReflectionUtils.makeAccessible(field);//装配“依赖”field.set(bean, value);} }
- 寻找出所有需要依赖注入的字段和方法,参考
- 说到这里,我们基本了解了 @Autowired 过程发生的位置和过程。而且很明显,我们案例中的错误就发生在上述“寻找依赖”的过程中(上述代码的第 9 行),那么到底是怎么发生的呢?我们可以继续刨根问底。
- 为了更清晰地展示错误发生的位置,我们可以采用调试的视角展示其位置(即
DefaultListableBeanFactory#doResolveDependency
中代码片段),参考下图:
- 如上图所示,当我们根据 DataService 这个类型来找出依赖时,我们会找出 2 个依赖,分别为 CassandraDataService 和 OracleDataService。在这样的情况下,如果同时满足以下两个条件则会抛出本案例的错误:
- 调用
determineAutowireCandidate
方法来选出优先级最高的依赖,但是发现并没有优先级可依据。具体选择过程可参考DefaultListableBeanFactory#determineAutowireCandidate
:protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {Class<?> requiredType = descriptor.getDependencyType();String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}// Fallbackfor (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||matchesBeanName(candidateName, descriptor.getDependencyName())) {return candidateName;}}return null; }
- 如代码所示,优先级的决策是先根据 @Primary 来决策,其次是 @Priority 决策,最后是根据 Bean 名字的严格匹配来决策。如果这些帮助决策优先级的注解都没有被使用,名字也不精确匹配,则返回 null,告知无法决策出哪种最合适。
- @Autowired 要求是必须注入的(即 required 保持默认值为 true),或者注解的属性类型并不是可以接受多个 Bean 的类型,例如数组、Map、集合。这点可以参考
DefaultListableBeanFactory#indicatesMultipleBeans
的实现:private boolean indicatesMultipleBeans(Class<?> type) {return (type.isArray() || (type.isInterface() &&(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)))); }
- 调用
- 对比上述两个条件和我们的案例,很明显,案例程序能满足这些条件,所以报错并不奇怪。而如果我们把这些条件想得简单点,或许更容易帮助我们去理解这个设计。就像我们遭遇多个无法比较优劣的选择,却必须选择其一时,与其偷偷地随便选择一种,还不如直接报错,起码可以避免更严重的问题发生。
问题修正
- 针对这个案例,有了源码的剖析,我们可以很快找到解决问题的方法:打破上述两个条件中的任何一个即可,即让候选项具有优先级或压根可以不去选择。不过需要你注意的是,不是每一种条件的打破都满足实际需求,例如我们可以通过使用标记 @Primary 的方式来让被标记的候选者有更高优先级,从而避免报错,但是它并不一定符合业务需求,这就好比我们本身需要两种数据库都能使用,而不是顾此失彼。
@Repository @Primary @Slf4j public class OracleDataService implements DataService{//省略非关键代码 }
- 现在,请你仔细研读上述的两个条件,要同时支持多种 DataService,且能在不同业务情景下精确匹配到要选择到的 DataService,我们可以使用下面的方式去修改:
@Autowired DataService oracleDataService;
- 如代码所示,修改方式的精髓在于将属性名和 Bean 名字精确匹配,这样就可以让注入选择不犯难:需要 Oracle 时指定属性名为 oracleDataService,需要 Cassandra 时则指定属性名为 cassandraDataService。
相关文章:
Spring Bean required a single bean, but 2 were found,发现多个 Bean
问题复现 在使用 Autowired 时,不管你是菜鸟级还是专家级的 Spring 使用者,都应该制造或者遭遇过类似的错误: required a single bean, but 2 were found 顾名思义,我们仅需要一个 Bean,但实际却提供了 2 个ÿ…...
用 Python 从零开始创建神经网络(十八):模型对象(Model Object)
模型对象(Model Object) 引言到目前为止的完整代码: 引言 我们构建了一个可以执行前向传播、反向传播以及精度测量等辅助任务的模型。通过编写相当多的代码并在一些较大的代码块中进行修改,我们实现了这些功能。此时,…...
Springboot 升级带来的Swagger异常
当升级到Springboot 2.6.0 以上的版本后,Swagger 就不能正常工作了, 启动时报如下错误。当然如果你再使用sping boot Actuator 和 Springfox, 也会引起相关的NPE error. (github issue: https://github.com/springfox/springfox/issues/3462) NFO | jvm 1 | 2022/04…...
【蓝桥杯研究生组】第15届Java试题答案整理
D 题 试题 D: 商品库存管理 时间限制: 3.0s 内存限制: 512.0MB 本题总分:10 分 【问题描述】 在库存管理系统中,跟踪和调节商品库存量是关键任务之一。小蓝经营的仓库中存有多种商品,这些商品根据类别和规格被有序地分类并编号,…...
数据结构(链式栈)
链式栈 链式栈(Linked Stack)是一种基于链表的数据结构,用于实现栈(后进先出,LIFO)的特性。与基于数组的栈不同,链式栈通过动态分配内存来存储数据,这使得它更加灵活,能…...
《代码随想录》Day22打卡!
回溯算法 《代码随想录》回溯算法:组合 本题完整题目如下: 本题的完整思路如下: 1.本题使用回溯算法,其实回溯和递归是一样的道理,也是分为三步曲进行: 2.第一步:确定递归函数的返回值和参数&…...
NetSuite Formula(HTML)超链打开Transaction
当Saved Search作为Sublist应用在Form时,如果Document Number是Group过的,则会出现如下超链失效的情况。 解决办法: 可以利用Saved Search中的Formula(HTML)功能来构建超链,用于打开Transaction。 以下图…...
传统听写与大模型听写比对
在快节奏的现代生活中,听写技能仍然是学习语言和提升认知能力的重要环节。然而,传统的听写练习往往枯燥乏味,且效率不高。现在,随着人工智能技术的发展,大模型听写工具的问世,为传统听写带来了革命性的变革…...
本地快速推断的语言模型比较:Apple MLX、Llama.cpp与Hugging Face Candle Rust
本地快速推断的语言模型比较:Apple MLX、Llama.cpp与Hugging Face Candle Rust 在自然语言处理(NLP)部署中,推断速度是一个关键因素,尤其是对于支持大型语言模型(LLM)的应用来说。随着Apple M1…...
Tomcat调优相关理解
什么是QPS? 是Queries Per Second 的缩写,是指服务器每秒查询数,比如定义一个a接口,该接口是10QPS,那么就是指该接口每秒可以处理10个请求 springboot默认并发处理数是多少? springboot并发处理要看serv…...
python爬虫--小白篇【selenium自动爬取文件】
一、问题描述 在学习或工作中需要爬取文件资源时,由于文件数量太多,手动单个下载文件效率低,操作麻烦,采用selenium框架自动爬取文件数据是不二选择。如需要爬取下面网站中包含的全部pdf文件,并将其转为Markdown格式。…...
Flink读写Kafka(DataStream API)
在Flink里,已经预定义了kafka connector,使用该connector我们可以读写kafka,并且能实现exactly once的语义。 要使用需要引入相关的maven依赖,在这里,因为读写kafka,就会涉及一个问题,kafka-client和broker的版本兼容问题,不过因为kafka client和broker的双向兼容的良…...
活动预告 | Microsoft 安全在线技术公开课:通过扩展检测和响应抵御威胁
课程介绍 通过 Microsoft Learn 免费参加 Microsoft 安全在线技术公开课,掌握创造新机遇所需的技能,加快对 Microsoft Cloud 技术的了解。参加我们举办的“通过扩展检测和响应抵御威胁”技术公开课活动,了解如何更好地在 Microsoft 365 Defen…...
nginx核心配置文件及常用功能
华子目录 配置文件说明配置文件格式说明nginx配置文件中的变量默认nginx.conf配置文件格式说明main全局配置events配置段 nginx配置中的root和aliaslocation用法详解虚拟主机配置nginx账户认证功能nginx自定义错误页面nginx自定义日志 配置文件说明 nginx官方帮助文档…...
基于AT89C51单片机的可暂停八路抢答器设计
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/90196607?spm1001.2014.3001.5503 C15 部分参考设计如下: 摘要 随着社会进步和科技发展,电子设备在各类活动中的应用日益普遍,…...
github加速源配置
访问github速度很慢? 试试一下方法 1: 编辑配置 vim /etc/docker/daemon.json 2:都复制粘贴上 { "registry-mirrors": [ "https://docker.211678.top", "https://docker.1panel.live…...
骑行解压:身心的奇妙之旅,VELO Angel Revo坐垫
在快节奏的都市生活中,骑行不仅是一种健康的生活方式,更是一种心灵的释放。从心理生理学的角度来看,骑行能够促使身体分泌内啡肽,带来愉悦感,同时,它还能转移注意力,缓解焦虑。在这场身心的奇妙…...
(七)- plane/crtc/encoder/connector objects
1,framebuffer/plane Rockchip RK3399 - DRM framebuffer、plane基础知识 - 大奥特曼打小怪兽 - 博客园 2,crtc Rockchip RK3399 - DRM crtc基础知识 - 大奥特曼打小怪兽 - 博客园 3,encoder/connector/bridge Rockchip RK3399 - DRM en…...
从零开始:如何在 .NET Core 中优雅地读取和管理配置文件
在.net中的配置文件系统支持丰富的配置源,包括文件(json、xml、ini等)、注册表、环境变量、命令行、Azure Key Vault等,还可以配置自定义配置源并跟踪配置的改变,然后按照优先级进行覆盖,总之对文件的配置有很多方法,这…...
Python中PDF转Word的技术
Python PDF转Word技术概述 在日常办公和数据处理中,经常需要将PDF文档转换为Word文档,以便进行编辑、修改或格式调整。Python作为一种强大的编程语言,提供了多种库和工具来实现这一功能。以下是对Python中PDF转Word技术的详细介绍。 一、技…...
挑战春招找到java后端实习第一天(1.1)
八股文 1.java中有哪些集合类请简单介绍一下 集合类分为两大类Collection和Map。前者是对象的集合,后者是键值对。 Collection分为List,Set,Queue三个接口。 List有LinkedList,ArrayList,Vector Set(不…...
leetcode hot 小偷
class Solution(object):def rob(self, nums):""":type nums: List[int]:rtype: int"""# 使用动态规划,把之前的给保存起来ans[0,nums[-1]]for i in range(1,len(nums)):ans.append(max(ans[-1],ans[-2]nums[-1*i-1]))return ans[-1]…...
一、Git与GitHub基础说明
Git与GitHub Git与GitHub一、Git1定义2核心功能(1) 版本控制(2) 分支管理(3) 合并操作 二、GitHub1定义2核心功能(1)远程仓库托管(2)Pull Requests(拉取请求)(3) Issue Tracking(问题跟踪)(4) 团队管理(5) 社交功能(6)个人资料和贡…...
Unity-Mirror网络框架-从入门到精通之Room示例
文章目录 前言Room示例场景设置NetworkRoomManagerSpawnerRewardRoomPlayerGamePlayer 最后 前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现…...
httpslocalhostindex 配置的nginx,一刷新就报404了
当你的Nginx配置导致页面刷新时报404错误时,通常是由于以下几个原因造成的: 静态文件路径配置错误:Nginx没有正确地指向静态文件的目录。前端路由问题:如果是SPA(单页应用),刷新页面时Nginx没有…...
Java重要面试名词整理(十九):Seata
文章目录 分布式事务概述实现思路:两阶段提交协议(2PC) SeataSeata的三大角色Seata的生命周期Seata解决方案 AT模式一阶段二阶段 XA模式TCC模式如何处理空回滚如何处理幂等如何处理悬挂 SAGA模式四种模式对比 分布式事务概述 在微服务架构中,完成某一个…...
OpenCV和PyQt的应用
1.创建一个 PyQt 应用程序,该应用程序能够: 使用 OpenCV 加载一张图像。在 PyQt 的窗口中显示这张图像。提供四个按钮(QPushButton): 一个用于将图像转换为灰度图一个用于将图像恢复为原始彩色图一个用于将图像进行翻…...
【Linux】进程间通信(一)
目录 一、进程间通信1.1 进程间通信目的1.2 理解进程间通信1.3 进程间通信发展1.4 进程间通信分类 二、管道2.1 什么是管道2.2 管道的原理2.3 匿名管道2.3.1 pipe函数2.3.2 匿名管道的实现2.3.3 匿名管道小结2.3.3.1 匿名管道的四种情况2.3.3.2 匿名管道的五种特性 2.3.4 匿名管…...
Fama MacBeth两步法与多因子模型的回归检验
Fama MacBeth两步法与多因子模型的回归检验 – 潘登同学的因子投资笔记 本文观点来自最近学习的石川老师《因子投资:方法与实践》一书 文章目录 Fama MacBeth两步法与多因子模型的回归检验 -- 潘登同学的因子投资笔记 多因子回归检验时序回归检验截面回归检验Fama–…...
Postman[4] 环境设置
作用:不同的环境可以定义不同的参数,在运行请求时可以根据自己的需求选择需要的环境 1.创建Environment 步骤: Environment-> ->命名->添加环境变量 2.使用Environment 步骤:Collection- >右上角选择需要的环境...
【paddle】初次尝试
张量 张量是 paddlepaddle, torch, tensorflow 等 python 主流机器学习包中唯一通货变量,因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…...
开源架构中的数据库选择优化版
上一篇文章推荐: 开源架构学习指南:文档与资源的智慧锦囊(New) 我管理的社区推荐:【青云交社区】和【架构师社区】 推荐技术圈福利社群:点击快速加入 开源架构中的数据库选择优化版 一、引言二、关系型开源…...
Echarts+vue电商平台数据可视化——webSocket改造项目
websocket的基本使用,用于测试前端能否正常获取到后台数据 后台代码编写: const path require("path"); const fileUtils require("../utils/file_utils"); const WebSocket require("ws"); // 创建WebSocket服务端的…...
【网络安全实验室】SQL注入实战详情
如果额头终将刻上皱纹,你只能做到,不让皱纹刻在你的心上 1.最简单的SQL注入 查看源代码,登录名为admin 最简单的SQL注入,登录名写入一个常规的注入语句: 密码随便填,验证码填正确的,点击登录…...
【信息系统项目管理师】第14章:项目沟通管理过程详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、规划沟通管理1、输入2、工具与技术3、输出二、管理沟通1、输入2、工具与技术3、输出三、监督沟通1、输入2、工具与技术3、输出一、规划沟通管理 定义:规划沟通管理是基于每个干系人或干系人群体的信息需求…...
YOLOv5部署到web端(flask+js简单易懂)
文章目录 前言最终实现效果图后端实现 主界面检测函数检测结果显示 前端实现 主界面(index.html)显示图片界面 总结 前言 最近,老板让写一个程序把yolov5检测模型部署到web端,在网页直接进行目标检测。经过1个星期的努力,终于实…...
什么是自治系统和非自治系统
自治系统 自治系统的特征是其状态方程不依赖于时间。举个简单的例子,考虑一阶常微分方程: d x d t − x \frac{dx}{dt} -x dtdx−x 这是一个经典的指数衰减过程,其中状态 (x) 随时间 (t) 衰减。这个系统是自治的,因为它的演…...
使用 CSS 的 `::selection` 伪元素来改变 HTML 文本选中时的背景颜色
定义 ::selection 伪元素: 在你的 CSS 文件中,添加 ::selection 伪元素,并设置 background-color 属性来改变选中文本的背景颜色。 示例代码: ::selection {background-color: yellow; /* 你可以根据需要更改颜色 */color: black…...
从0入门自主空中机器人-3-【环境与常用软件安装】
关于本课程: 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程,包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程,带你从0开始,组装属于自己的自主无人机,并让…...
jmeter分布式启动
https://www.cnblogs.com/qtclm/p/11082081.html 1、代理机:输入“ipconfig”,找到IP地址,在Jmeter/bin/jmeter.properties设置remote host 启动jmeter server 1、控制机:输入“ipconfig”,找到IP地址,在J…...
【Linux】HTTP cookie与session
在登录B站时,有登录和未登录两种状态, 问题:B站是如何认识我这个登录用户的?问题:HTTP是无状态、无连接的,怎么能够记住我? HTTP协议是无状态、无连接的。比如客户端(浏览器&#…...
20. 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--补充--自动添加接口地址
在同学学习过程,部分同学向我反馈说每次新增接口都要在接口表里手动添加一条接口很麻烦,因此我把项目代码做了一个改动,使我们不需要手动添加,每次项目运行起来后就会自动把新的接口地址添加进去。 一、实现 首先,我…...
[Linux] 服务器CPU信息
(1)查看CPU信息(型号) cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c输出:可以看到有128个虚拟CPU核心,型号是后面一串 128 Intel(R) Xeon(R) Platinum 8336C CPU 2.30GHz(2&…...
java_使用阿里云oss服务存储图片
什么情况下可以使用阿里云oss服务存储图片? 对图片的访问速度有高要求时使用,方便用户快速的(比如在网页页面中)访问到图像 参考:41 尚上优选项目-平台管理端-商品信息管理模块-阿里云OSS介绍_哔哩哔哩_bilibili 1.…...
Dali 1.1.4 | 解锁版AI图像生成器,无限生成
Dali是一款先进的AI图像生成器应用程序,能够根据您的描述生成不同风格的独特图像。它不仅限于生成艺术作品,还可以创建创新的纹身设计、独一无二的标志以及超写实照片。该软件使用尖端技术,将想象力转化为现实,提供迷人的数字艺术…...
快手视频不让下载怎么保存到相册
快手,作为国内领先的短视频平台之一,吸引了无数用户发布创意视频、分享生活点滴。随着短视频版权保护和用户隐私问题的日益严重,越来越多的视频内容在平台内都采取了“不让下载”的限制。面对这一情况,很多用户都希望能够保存自己…...
Linux环境下CUDA与对应版本CuDNN的安装指南
转载:Linux环境下CUDA与对应版本CuDNN的安装指南-百度开发者中心...
mybatisPlus打印sql配置
MyBatis-Plus 提供了方便的配置方式来打印 SQL 查询语句,以便进行调试和性能分析。可以通过配置 log 来输出 SQL 语句以及执行的参数。 方法 1:通过 application.properties 或 application.yml 配置打印 SQL 可以通过配置 application.properties 或 a…...
InstructGPT:基于人类反馈训练语言模型遵从指令的能力
大家读完觉得有意义记得关注和点赞!!! 大模型进化树,可以看到 InstructGPT 所处的年代和位置。来自 大语言模型(LLM)综述与实用指南(Amazon,2023) 目录 摘要 1 引言 …...
曾仕强解读《易经》
曾仕强对《易经》的解读内容丰富、深入浅出,以下是一些主要方面: 讲解《易经》基本原理 - 阴阳之道:曾仕强将阴阳比作白天与黑夜、男人与女人等,指出阴阳看似对立,实则相辅相成,强调为人处世要把握阴阳…...