Spring是怎么扫描这些Bean的
一、总览
这个问题问得非常棒!这就开始进入Spring的核心机制之一了:组件扫描(Component Scan)机制。
✅ 一句话总结
Spring是通过包扫描(Component Scanning)的方式,在项目启动时自动找到标注了@Component、@Service、@Repository、@Controller等注解的类,并把它们注册为Bean,放进Spring容器中管理。
🔍 整体流程分 3 步走:
1️⃣ 开启组件扫描:告诉Spring去哪里找Bean
通常是在Spring Boot启动类或XML配置里写:
👉 Spring Boot 示例:
@SpringBootApplication // 内部包含 @ComponentScan public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
默认扫描启动类所在包及其子包下的所有类
你也可以显式指定扫描包路径:
@ComponentScan(basePackages = {"com.example.service", "com.example.dao"})
2️⃣ 使用注解标记要被扫描的类
Spring会识别这些注解,把类注册成Bean:
@Component | 泛指组件(最基础的注解) |
@Service | 业务逻辑组件 |
@Repository | DAO 数据访问组件 |
@Controller | 控制器,Web请求入口 |
这些其实都是@Component的"语义化子注解"。
3️⃣ Spring 在启动时会做这些事:
在启动期间,Spring会:
- 扫描你指定的包路径下的所有class文件
- 找出被这些注解标记的类
- 通过反射把它们实例化(使用构造器)
- 把它们注册为Bean,放进Spring容器
- 可以通过@Autowired或@Resource来自动注入它们
🧠 Spring 是怎么实现的?(原理底层)
组件扫描是由ClassPathBeanDefinitionScanner这个类完成的,它会:
- 根据你配置的base-package找到所有.class 文件
- 读取这些类的注解(基于反射 + ASM 字节码工具)
- 如果发现类上有@Component 或其子注解,就创建一个 BeanDefinition
- 把这些BeanDefinition注册到Spring的容器中
最终你调用ApplicationContext.getBean(...)或使用@Autowired就能拿到它。
🎯 小例子回顾
@ComponentScan(basePackages = "com.mycompany") public class AppConfig {} @Service public class MyService {} @RestController public class MyController { @Autowired private MyService myService; }
启动时会自动发现MyService,并把它注入到MyController中。
🧪 想验证扫描效果怎么办?
你可以在Spring Boot启动日志中搜索:
Creating shared instance of singleton bean 'myService'
或者开启Bean打印日志,看看你的类有没有被注册成功。
✅ 总结
1. 配置扫描路径 | 用 @ComponentScan或@SpringBootApplication |
2. 加注解标记类 | 用@Component/@Service/@Controller等 |
3. 启动时扫描 | Spring利用反射+ASM扫描并注册为Bean |
4. 容器管理 | 可以用@Autowired自动注入了 |
二、只有这四种@Component、@Service、@Repository DAO 数据访问组件、@Controller会被SpringBoot识别并注册成Bean吗?是没有别的了吗?
你提到的那四种注解(@Component、@Service、@Repository、@Controller)确实是最常见的被SpringBoot自动扫描并注册成Bean的注解,但不是"只有这四种"。
接下来我给你详细拆解下:
✅ 1.这四个是最常见的"元注解"
@Component | 通用组件 | ✅ 基础注解 |
@Service | 业务逻辑层 | ✅ 是@Component的派生 |
@Repository | DAO 数据层 | ✅ 是@Component的派生 |
@Controller | 控制器层 | ✅ 是@Component的派生 |
也就是说,它们本质上都是@Component的语义包装,所以SpringBoot能识别它们。
✅ 2.还有这些注解,也会被注册为Bean!
1️⃣ @RestController
- 是@Controller + @ResponseBody的组合,用于REST风格接口。
- 也会被自动注册为Bean。
2️⃣ @Configuration
- 表示这是一个配置类,Spring会把它注册为Bean。
- 类似传统的 XML <beans> 配置。
@Configuration public class AppConfig {}
3️⃣ @Bean
- 用于手动注册Bean(不是自动扫描的方式)。
- 放在 @Configuration 类中,表示方法返回值就是一个 Bean。
@Bean public MyService myService() { return new MyService(); }
4️⃣ @ComponentScan
- 它本身不是注册Bean的,而是用来指定哪些包路径需要被扫描。
✅ 3.自定义注解也可以!
你甚至可以自定义一个注解,然后加上@Component元注解,让Spring也能扫描它:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component public @interface MyCustomComponent { }
然后你可以像这样用:
@MyCustomComponent public class MyHandler {}
Spring 也会把它识别成一个 Bean。
✅ 4.SpringBoot还有自动注册机制(自动装配)
除了扫描注解以外,SpringBoot 还有一些更高级的注册方式:
1. @EnableAutoConfiguration + SpringFactories
- Spring Boot会从META-INF/spring.factories或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports里找自动配置类。
- 比如你引入了spring-boot-starter-web,就自动给你注册了DispatcherServlet、Tomcat、Jackson等Bean。
🎯 小结:除了你说的四个注解,还有这些也会注册为Bean
@Component | ✅ 是 | 最基础的 |
@Service | ✅ 是 | 业务层 |
@Repository | ✅ 是 | DAO 层 |
@Controller | ✅ 是 | 控制器层 |
@RestController | ✅ 是 | REST 风格控制器 |
@Configuration | ✅ 是 | 配置类 |
@Bean | ✅ 手动注册 | 用在配置类方法上 |
自定义注解 | ✅(如果带@Component) | 也能被扫描 |
自动配置类 | ✅ | Spring Boot 特有机制 |
三、所以所有带@符号的注解,都会在启动时被SpringBoot扫描到并注册成Bean对吗?
结论是:❌不是所有带@符号的注解都会被Spring Boot扫描并注册成Bean。
✅ 先说重点答案
只有特定注解 + 特定位置 + 特定配置下的类,才会被Spring Boot扫描并注册成Bean。
❗那到底哪些注解会被注册成 Bean 呢?
只有那些本身标注了@Component或间接标注了它的注解,Spring Boot才会识别并注册为Bean。
比如:
注解 | 本质是否是@Component? | 能否自动注册为Bean | 用途说明 |
@Component | ✅ | ✅ | 通用组件 |
@Service | ✅(是@Component的子注解) | ✅ | 业务逻辑层 |
@Repository | ✅ | ✅ | DAO 数据层 |
@Controller | ✅ | ✅ | 控制器层 |
@RestController | ✅(组合注解) | ✅ | REST 控制器 |
@Configuration | ✅(底层是@Component) | ✅ | 配置类 |
@Bean | ❌ | ✅(注册方法返回值) | 配置类中的注册方式 |
@RequestMapping / @PostMapping | ❌ | ❌ | 是功能注解,不是 Bean 注解 |
@Autowired | ❌ | ❌ | 是注入工具,不是 Bean 定义 |
@Transactional | ❌ | ❌ | 是切面功能,不注册 Bean |
✅ Spring Boot是怎么判断一个类是不是Bean的?
Spring Boot的自动扫描器(ClassPathBeanDefinitionScanner)在扫描类时,只会找那些带有@Component或其派生注解的类。
也就是说,SpringBoot 启动后:
- 会扫描指定的包路径
- 对每个类进行"注解判断"
- 如果发现类上有@Component或它的派生注解就生成一个BeanDefinition注册进Spring 容器
其它的注解,比如@RequestMapping、@Autowired、@Getter(Lombok)、@Slf4j、@NotNull、@Override等,只是功能型注解,Spring并不会把这些类注册成Bean。
🧠 举个反例说明
public class TestTool { @PostConstruct public void init() { System.out.println("init..."); } }
这个类没有加任何@Component或其子注解,它不会被Spring注册为Bean,即使它有很多带@的注解,比如@PostConstruct、@Slf4j、@Data,也没用。
✅ 想让一个类被注册为Bean,必须满足以下条件之一:
方式 | 说明 |
类上加了 @Component或其子注解 | 最推荐 |
被@Bean方法返回 | 用在配置类里注册第三方类 |
通过XML配置 | 传统方式,现在不常用了 |
被SpringBoot自动装配机制注册 | 如引入starter自动配置类 |
📌 总结回顾
说法 | 正确吗 | 原因 |
所有带@的注解都会让类变成 Bean? | ❌ 错 | 只有@Component及其子注解才行 |
想让一个类被Spring容器管理,必须加啥? | ✅ 加@Component或子注解,或用@Bean注册 | |
@Autowired的类一定是Bean? | ✅ 被注入的对象必须是Bean,使用注入的地方也必须是Bean |