Spring Bean 为何“难产”?攻克构造器注入的依赖与歧义

news2025/7/26 22:12:10

本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!

  • 🚀 魔都架构师 | 全网30W技术追随者
  • 🔧 大厂分布式系统/数据中台实战专家
  • 🏆 主导交易系统百万级流量调优 & 车联网平台架构
  • 🧠 AIGC应用开发先行者 | 区块链落地实践者
  • 🌍 以技术驱动创新,我们的征途是改变世界!
  • 👉 实战干货:编程严选网

1 案例:定义的 Bean 缺少隐式依赖

有时把一个类定义成 Bean,又觉得这Bean定义除了加些 Spring 注解,好像平淡无奇!以致在后续使用时随心定义:

package com.javaedge.spring.service;

@Service
public class MyService {

    private String myServiceName;

    public MyService(String myServiceName) {
        this.myServiceName = myServiceName;
    }

    public String sayHello() {
        return "hello Java";
    }
}

MyService显式定义了一个构造器。但上面代码不是永远都能正确运行,有时报错:

***********************************
APPLICATION FAILED TO START
***********************************

Description:

Parameter 0 of constructor in com.javaedge.spring.service.MyService required a bean of type 'java.lang.String' that could not be found.

The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:

Consider defining a bean of type 'java.lang.String' in your configuration.

2 解惑

创建一个 Bean 时,调AbstractAutowireCapableBeanFactory的

2.1 createBeanInstance

  1. 寻找构造器
  2. 通过反射调用构造器创建实例
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
      mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
   return autowireConstructor(beanName, mbd, ctors, args);
}

2.2 determineConstructorsFromBeanPostProcessors

Spring先执行它获取构造器:

仅一个有效实现类。然后通过 autowireConstructor 带着构造器去创建实例。

看ConstructorResolver的

2.3 instantiate

autowireConstructor方法要创建实例:

  • 不仅要知道是啥构造器
  • 还要知道构造器对应参数

从最后创建实例的方法名也可看出:

private Object instantiate(
      String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) 

argsToUse咋获取?即当已知构造器ServiceImpl(String serviceName),要创建 ServiceImpl 实例,咋确定serviceName值?

使用 Spring,不能直接显式 new 创建实例。Spring只能寻找依赖作为构造器调用参数。那这参数咋获取?

看ConstructorResolver的

2.4 autowireConstructor

argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
								getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);

可调用 createArgumentArray 构建调用构造器的参数数组,最终从 BeanFactory 获取 Bean:

即根据参数寻找对应 Bean。本案例中,如找不到对应 Bean,抛异常,提示装配失败。

3 修正

定义一个类为 Bean,若再显式定义构造器,则该 Bean 在构建时,会自动根据构造器参数定义寻找对应 Bean,反射创建该 Bean。可直接定义一个能让 Spring 装配给 MyService 构造器参数的 Bean,如:

package com.javaedge.spring.service;

@Service
public class MyService {

    private String myServiceName;

    public String sayHello() {
        return "hello Java";
    }

    // 该bean装配给MyService的构造器参数-myServiceName
    @Bean
    public String myServiceName() {
        return "MyServiceName";
    }
}

综上,使用 Spring 时,别想当然认为定义的 Bean 也可在非 Spring 场合直接new!

若不精通 Spring 的隐式规则,在修正问题后,可能写出更多看起来好像可运行的程序:

@Service
public class MyService {

    private String myServiceName;

    public MyService(String myServiceName) {
       this.myServiceName = myServiceName;
    }

    public MyService(String myServiceName, String otherParam) {
       this.myServiceName = myServiceName;
    }

review这段代码,可能不会发现什么问题,毕竟 String 类型可自动装配,无非加个 String 参数。但若你知 Spring 内部用反射构建 Bean,就发现问题:存在两个构造器,都可调用时,到底调用哪个?最终 Spring 无从选择,只能尝试调用默认构造器,而默认构造器不存在:

所以报错:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2399511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Lecture01】动手开发科研智能体(WIN11系统)

1. 配置win11系统中的环境&#xff0c;安装管理器Choco&#xff1a; # Download and install Chocolatey: powershell -c "irm https://community.chocolatey.org/install.ps1|iex" # Download and install Node.js: choco install nodejs-lts --version"22&qu…

“packageManager“: “pnpm@9.6.0“ 配置如何正确启动项目?

今天在学习开源项目的时候&#xff0c;在安装依赖时遇到了一个报错 yarn add pnpm9.6.0 error This projects package.json defines "packageManager": "yarnpnpm9.6.0". However the current global version of Yarn is 1.22.22.Presence of the "…

Paraformer分角色语音识别-中文-通用 FunASR

https://github.com/modelscope/FunASR/blob/main/README_zh.md https://github.com/modelscope/FunASR/blob/main/model_zoo/readme_zh.md PyTorch / 2.3.0 / 3.12(ubuntu22.04) / 12.1 Paraformer分角色语音识别-中文-通用 https://www.modelscope.cn/models/iic/speech_p…

Spitfire:Codigger 生态中的高性能、安全、分布式浏览器

Spitfire 是 Codigger 生态系统中的一款现代化浏览器&#xff0c;专为追求高效、隐私和分布式技术的用户设计。它结合了 Codigger 的分布式架构优势&#xff0c;在速度、安全性和开发者支持方面提供了独特的解决方案&#xff0c;同时确保用户对数据的完全控制。 1. 高性能浏览…

运行shell脚本时报错/bin/bash^M: 解释器错误: 没有那个文件或目录

Windows的换行符为\r\n&#xff0c;而linux换行符为\n。先查看一下文件是什么格式的 :set ff --查询一下格式是什么 由于使用nodepad新建的脚本&#xff0c;首选项中格式设置成了windows&#xff0c;上传到linux中报错。 解决方法 1、nodepad中【设置》首选项】修改为unix&am…

Shiro安全权限框架

①、添加依赖 ②、创建ini文件 获取权限相关信息可以通过数据库获取&#xff0c;也可以通过ini配置文件获取 ③、认证代码 public class ShiroRun{public static void main(){//初始化获取SecurityManagerIniSerucityManagerFactory factory new IniSecurityManagerFac…

虚拟现实教育终端技术方案——基于EFISH-SCB-RK3588的全场景国产化替代

一、VR教育终端技术挑战与替代价值 ‌实时交互性能瓶颈‌ 赛扬N100/N150仅支持3DOF渲染&#xff08;延迟&#xff1e;25ms&#xff09;&#xff0c;动态手势识别帧率≤15FPS&#xff0c;难以满足6DOF教学场景需求RK3588 Mali-G610 GPU支持6DOF空间渲染&#xff08;延迟≤12ms&…

网络安全A模块专项练习任务五解析

任务五:Linux 操作系统安全配置-1 任务环境说明: ✓ 服务器场景:LinuxServer:(开放链接) ✓ 用户名:root&#xff0c;密码:123456 ✓ 数据库用户名:root&#xff0c;密码:123456 请对服务器 LinuxServer 按要求进行相应的设置&#xff0c;提高服务器的安全性。 1.设置最小…

Redis初入门

Nosql&#xff1a;Not-Only SQL&#xff08;泛指非关系型数据库&#xff09;&#xff0c;作为关系型数据库的补充 作用&#xff1a;应对基于海量用户和海量数据前提下的数据处理问题 redis&#xff1a;C语言开发的一个开源的高性能键值对数据库 特征&#xff1a; 1、数据之…

(10)Fiddler抓包-Fiddler如何设置捕获Firefox浏览器的Https会话

1.简介 经过上一篇对Fiddler的配置后&#xff0c;绝大多数的Https的会话&#xff0c;我们可以成功捕获抓取到&#xff0c;但是有些版本的Firefox浏览器仍然是捕获不到其的Https会话&#xff0c;需要我们更进一步的配置才能捕获到会话进行抓包。 2.环境 1.环境是Windows 10版…

使用pandas实现合并具有共同列的两个EXCEL表

表1&#xff1a; 表2&#xff1a; 表1和表2&#xff0c;有共同的列“名称”&#xff0c;而且&#xff0c;表1的内容&#xff08;行数&#xff09;<表2的行数。 目的&#xff0c;根据“名称”列的对应内容&#xff0c;将表2列中的“所处行业”填写到表1相应的位置。 实现代…

2025年- H69-Lc177--78.子集(回溯,组合)--Java版

1.题目描述 2.思路 3.代码实现 class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> resnew ArrayList<>();List<Integer> curnew ArrayList<>();//从索引0开始递归backtracking(res,cur,nums,0…

目标检测任务的评估指标mAP50和mAP50-95

mAP50 和 mAP50-95 是目标检测任务中常用的评估指标&#xff0c;用于衡量模型在不同 交并比&#xff08;IoU&#xff09;阈值 下的平均精度&#xff08;Average Precision, AP&#xff09;。它们的区别主要体现在 IoU 阈值范围 上。 ✅ 1. mAP50&#xff08;mean Average Prec…

C++String的学习

1、C语言中的字符串 C语言中&#xff0c;字符串是以’\0’结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xff08;即面向对象编程&#xff08;…

java day15 (数据库)

进入数据库的学习 DB 因为数据太多了&#xff0c;方便统一管理的软件 操作就不用改代码了&#xff0c;直接改数据库则可&#xff1b; 命令就是sql语句 这些都是关系型数据库&#xff0c;sql可以控制全部&#xff0c;至于具体的环境我以前就有安装过了&#xff1b; 理解&am…

多线程爬虫使用代理IP指南

多线程爬虫能有效提高工作效率&#xff0c;如果配合代理IP爬虫效率更上一层楼。作为常年使用爬虫做项目的人来说&#xff0c;选择优质的IP池子尤为重要&#xff0c;之前我讲过如果获取免费的代理ip搭建自己IP池&#xff0c;虽然免费但是IP可用率极低。 在多线程爬虫中使用代理I…

前端面试真题(第一集)

目录标题 1、跨域问题及解决方法同源策略生产环境解决方案开发环境解决方案其他解决方案 2、组件间通信方式Vue2中的组件通信方式Vue3中的组件通信方式通用注意事项 3、微信小程序生命周期微信小程序原生生命周期UniApp生命周期 4、微信小程序授权登录流程登录流程手机号获取 5…

TDengine 高级功能——流计算

简介 在时序数据的处理中&#xff0c;经常要对原始数据进行清洗、预处理&#xff0c;再使用时序数据库进行长久的储存&#xff0c;而且经常还需要使用原始的时序数据通过计算生成新的时序数据。在传统的时序数据解决方案中&#xff0c;常常需要部署 Kafka、Flink 等流处理系统…

05.字母异位词分组

题意理解 &#x1f9e0; 什么是“字母异位词”&#xff1f; 字母异位词是指由相同的字母组成&#xff0c;只是排列顺序不同的单词。 比如&#xff1a; "eat" 和 "tea" 是异位词&#xff0c;它们都包含 e、a 和 t。"ate" 也是它们的异位词。但…

Mac查看MySQL版本的命令

通过 Homebrew 查看&#xff08;如果是用 Homebrew 安装的&#xff09; brew info mysql 会显示你安装的版本、路径等信息。 你的终端输出显示&#xff1a;你并没有安装 MySQL&#xff0c;只是查询了 brew 中的 MySQL 安装信息。我们一起来看下重点&#xff1a; &#x1f9fe…