当前位置: 首页 > news >正文

Spring Bean扫描

好的,没有问题。基于我们之前讨论的内容,这是一篇关于 Spring Bean 扫描问题的深度解析博客。


Spring Bean扫描

作者:Gz | 发布时间:2025年9月9日

🎯 Spring如何找到你的Bean?

首先要理解原理。Spring的组件扫描主要依赖于@ComponentScan注解。

在现代Spring Boot应用中,你通常看不到@ComponentScan,因为它已经被包含在了@SpringBootApplication注解中。

@SpringBootApplication // <-- 这个注解里面其实包含了 @ComponentScan
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}

默认情况下,@SpringBootApplication会扫描其所在包以及所有子包下的所有组件。
在这里插入图片描述
例如直接把图片中的dao软件包移动到itheima下面,然后启动程序就会出现扫描不到错误
在这里插入图片描述
在对应类没有加注解也会出现报错
在这里插入图片描述
在这里插入图片描述

com.example.myapp      <-- 启动类所在的根包
├── MyApplication.java   <-- @SpringBootApplication 在这里
├── controller
│   └── UserController.java  (@RestController)
├── service
│   └── UserServiceImpl.java (@Service)
└── repository└── UserRepositoryImpl.java (@Repository)

在这个结构下,controller, service, repository 都是根包 com.example.myapp 的子包,所以它们的组件都能被自动发现。

🔍 常见问题与解决方案

问题一:NoSuchBeanDefinitionException - “我的Bean去哪了?”

这是最常见的错误,意味着Spring在需要注入一个Bean时,在容器里找不到它。

原因1:忘记添加组件注解

这是最粗心也最常见的错误。你创建了一个类,但忘记用@Component, @Service, @Repository, @Controller等注解标记它。

解决方案:检查你的类,确保它有相应的组件注解。

// ❌ 错误:这个类不会被Spring发现
public class UserServiceImpl implements UserService { ... }// ✅ 正确:添加@Service注解
@Service
public class UserServiceImpl implements UserService { ... }
原因2:组件不在默认的扫描路径下

这是初学者最容易犯的错误。你的组件类所在的包,不是启动类所在包的子包。

示例:错误的包结构

com
├── example
│   └── myapp            <-- 启动类在这里
│       └── MyApplication.java
└── other└── utils            <-- 工具类想被注入,但它不在扫描路径下└── MyUtil.java (@Component)

解决方案A (推荐)遵循规范,将包移动到启动类所在包的子包下。这是最符合Spring Boot“约定优于配置”思想的做法。

解决方案B (特殊情况使用):在启动类上显式指定要扫描的包

@SpringBootApplication
@ComponentScan(basePackages = {"com.example.myapp", "com.other.utils"})
public class MyApplication { ... }

问题二:NoUniqueBeanDefinitionException - “Bean太多了,我该选哪个?”

这个错误与找不到Bean正好相反:Spring找到了多个符合注入要求的Bean,导致它不知道该注入哪一个。

原因:一个接口有多个实现类

假设我们有一个NotificationService接口,同时有两个实现:EmailServiceSmsService

public interface NotificationService {void send(String message);
}@Service("emailNotification")
public class EmailService implements NotificationService { ... }@Service("smsNotification")
public class SmsService implements NotificationService { ... }

当你尝试注入时,就会出现问题:

@Autowired
private NotificationService notificationService; // <-- Spring懵了:你想要Email还是SMS?

** 🎯:@Primary指定首选项**
给其中一个实现类加上@Primary注解,告诉Spring如果遇到多个选项,优先注入这一个。

@Service("emailNotification")
@Primary // <-- 默认情况下,注入这一个
public class EmailService implements NotificationService { ... }

解决方案B:使用@Qualifier精确指定
在注入点使用@Qualifier注解,通过Bean的名称来精确指定你想要注入哪一个实现。

@Autowired
@Qualifier("smsNotification") // <-- 我明确想要注入名为 "smsNotification" 的Bean
private NotificationService notificationService;

🎯 @Resource注解总结

📋 @Resource 是什么?

@ResourceJava标准注解(JSR-250规范),用于依赖注入,由Java EE提供,不是Spring特有的。

@Resource
private UserDao userDao;  // 按类型注入@Resource(name = "userDaoImpl")  
private UserDao userDao;  // 按名称注入

🔍 @Resource vs @Autowired 对比

特性@Resource@Autowired
来源Java标准注解Spring特有注解
包名jakarta.annotation.Resourceorg.springframework.beans.factory.annotation.Autowired
注入策略先按名称,再按类型先按类型,再按名称
支持@Qualifier❌ 不支持✅ 支持
支持required属性❌ 不支持✅ 支持
适用场景标准Java EE项目Spring项目

💡 使用建议:

  1. Spring项目推荐@Autowired(更灵活)
  2. Java EE标准@Resource(更好的移植性)
  3. 按名称注入@Resource(name="xxx")
  4. 按类型注入@Autowired@Resource

💡 最佳实践

  1. 遵循标准项目结构
    将你的启动类放在一个顶层的根包中,所有其他业务代码都放在这个根包的子包里。这是解决扫描问题的最佳“预防针”。

  2. 显式处理多实现
    当你知道一个接口会有多个实现时,不要等到报错。主动使用@Primary@Qualifier来明确依赖关系,让代码意图更清晰。

  3. 优先使用@Service, @Repository, @Controller
    虽然@Component也行,但使用更具体的注解能让代码分层更明确,并且可以利用到@Repository的异常转译等额外功能。

  4. 谨慎使用@ComponentScan
    只有当你确实需要包含非标准路径下的组件时,才显式使用@ComponentScan。在大多数Spring Boot项目中,你根本不需要写这个注解。

🎯 总结

  1. 扫描不到Bean (NoSuchBeanDefinitionException):首先检查①是否忘记注解,其次检查②是否在扫描路径下
  2. Bean不唯一 (NoUniqueBeanDefinitionException):使用**@Primary指定默认实现,或使用@Qualifier**精确注入。
  3. @SpringBootApplication:它的位置决定了默认的扫描根路径,至关重要。
    理解了Spring组件扫描的原理和这几个常见问题的模式后,你就能在遇到问题时从容应对,快速定位并解决问题。一个结构清晰、扫描路径明确的项目,是构建健壮、可维护应用的第一步。

文章转载自:

http://zZTC4qVG.cLxpp.cn
http://ZBnZZEP0.cLxpp.cn
http://dl4oeAgr.cLxpp.cn
http://TyKSSwM9.cLxpp.cn
http://SpMJJEL4.cLxpp.cn
http://5Gi7VUrz.cLxpp.cn
http://iHjxUzS0.cLxpp.cn
http://n3nnTBPN.cLxpp.cn
http://lbde2bJG.cLxpp.cn
http://qMI3deR0.cLxpp.cn
http://02QXILzt.cLxpp.cn
http://3AkO2avQ.cLxpp.cn
http://4Kijrhtd.cLxpp.cn
http://wSKJ4wkx.cLxpp.cn
http://7Y2VgxuV.cLxpp.cn
http://RNqCNmpb.cLxpp.cn
http://Y4ORKkv7.cLxpp.cn
http://KWBStVoP.cLxpp.cn
http://XxPeIct3.cLxpp.cn
http://DIuJ0VjC.cLxpp.cn
http://nhQmvJxY.cLxpp.cn
http://j0X6jrs4.cLxpp.cn
http://lco72lav.cLxpp.cn
http://JgwB6HrV.cLxpp.cn
http://WlzVgYE6.cLxpp.cn
http://gBjXDCVQ.cLxpp.cn
http://VsCUPsmm.cLxpp.cn
http://1awZQyZT.cLxpp.cn
http://i1SEzG8x.cLxpp.cn
http://TewDmdJg.cLxpp.cn
http://www.dtcms.com/a/376815.html

相关文章:

  • 第2讲 机器学习 - 导论
  • 【开题答辩全过程】以 基于Android的智慧旅游APP开发为例,包含答辩的问题和答案
  • Linux服务器的系统安全强化超详细教程
  • Dockerfile构建容器需要注意的事项。
  • YOLO 发展前景与创新点
  • 一个基于 .NET 开源、轻便的 Windows 优化工具,适用于 Win7 - Win11 最新版的优化!
  • RL【7-1】:Temporal-difference Learning
  • child_process 和 cluster的区别
  • 第十七篇|优尼塔斯东京校区的教育数据工程:学费函数、国籍网络与升学有向图
  • ES6 面试题及详细答案 80题 (33-40)-- Symbol与集合数据结构
  • DeepResearch(上)
  • 即时通讯小程序
  • Firefox Window 开发详解(二)
  • Chrome性能黑魔法:深入浅出PGO优化与实战指南
  • 【算法专题训练】20、LRU 缓存
  • 66. 加一 (编程基础0到1)(Leetcode)
  • 多任务相关概念
  • ubuntu 18.04 泰山派编译报错
  • 解决apk包体大于2G无法对齐和签名的问题
  • 运筹学——运输问题之表上作业法,西北角法,最小元素法
  • python版本管理和依赖管理的最佳实践,pyenv + uv
  • iPhon 17 推出
  • MySQL的常用命令
  • KEDA/HPA/VPA 三件套:ABP 后台作业的事件驱动伸缩
  • 金融中的异常收益率
  • 模型部署:(三)安卓端部署Yolov8-v6.0目标检测项目全流程记录
  • 阅读|史蒂芬·普拉达《C Primer Plus(第6版)》:数据和C
  • 回归预测 | MATLAB基于GRU-Attention的多输入单输出回归预测
  • UniApp 分包异步化配置及组件引用解决方案
  • Postman环境变量全局变量设置