回答这个问题之前我们一定要了解 Spring Boot “约定优于配置”的理念。
答案的核心是两个概念:组件扫描(Component Scanning) 和 自动配置(Auto-Configuration)。
我们来一步步揭开这个答案。
背后的原理:我们用一个“人口普查”的案例来分析
假设 Spring Boot 启动的过程就像一个城市(你的应用程序)进行第一次人口普查。
-
第一步:居民亮出身份牌 (
@Component,@Service等)
你在你的类上写的@Service,@Component,@Repository,@Controller等注解,就像是给你的房子门口挂上了一个牌子,上面写着:“我是本市合法居民,请登记我!”。@Component是最通用的牌子。@Service、@Repository、@Controller是更具体的牌子(“我是医生”、“我是工程师”、“我是市长”),它们本质上都包含了@Component的功能,只是语义上更清晰。
-
第二步:普查员出动 (
@ComponentScan)
光有牌子没用,还需要有人去看。Spring 框架有一个“普查员”,它的名字叫@ComponentScan。这个注解的作用就是告诉 Spring:“请到指定的区域去扫描所有挂了‘居民’牌子的类!” -
第三步:普查的起点和范围 (神奇的
@SpringBootApplication)
这时你可能会问:“可我没写@ComponentScan啊?”
这正是 Spring Boot 的第一个魔法。你启动类上那个熟悉的@SpringBootApplication注解,其实是一个“三合一”的复合注解,它包含了:@SpringBootConfiguration:表示这是一个 Spring Boot 的配置类。@EnableAutoConfiguration:启用 Spring Boot 的自动配置功能(这是另一个大魔法,暂且不表)。@ComponentScan:这就是答案!@SpringBootApplication默认就帮你开启了组件扫描!
那么,扫描的范围是哪里呢?
约定: Spring Boot 默认会扫描@SpringBootApplication所在类及其所有子包(sub-packages)。这就是为什么我们通常会把启动类放在项目的根包(root package)下(比如
com.myapp),这样它就能自动扫描到com.myapp.service,com.myapp.controller等所有子包里的组件了。com.myapp ├── Application.java <-- @SpringBootApplication 在这里,是扫描的起点 │ ├── controller │ └── MyController.java <-- 能被扫描到 │ ├── service │ └── MyService.java <-- 能被扫描到 │ └── repository └── MyRepository.java <-- 能被扫描到 -
第四步:登记入册(注册到 IoC 容器)
普查员 (@ComponentScan) 扫描到所有挂牌的类之后,并不会立刻使用它们。它会把这些类的“信息”报告给 Spring IoC 容器。
容器拿到这份名单后,会通过**反射(Reflection)**机制来创建这些类的实例,并将这些实例作为 Bean 登记在册,存放在一个类似Map的结构中进行统一管理。 -
第五步:按需分配(
@Autowired依赖注入)
现在,容器里已经有了一堆注册好的、随时可用的 Bean(比如myServiceImpl的实例)。
当容器在创建另一个 Bean(比如MyController)时,它会检查这个类的代码:- 它发现
MyController有一个字段或构造函数上标注了@Autowired private MyService myService;。 - 这个注解的意思是:“我需要一个
MyService类型的居民来帮我干活!” - 于是,容器就去它的“登记册”(那个
Map)里查找一个类型是MyService的 Bean。 - 找到后,就把它“注入”(赋值)给
MyController的myService字段。
- 它发现
至此,整个链路就完成了!
流程总结
Spring Boot 启动时:
- 启动 -> 看到主启动类上的
@SpringBootApplication。 - 发现
@ComponentScan-> 从@SpringBootApplication中发现内置的@ComponentScan。 - 确定扫描路径 -> 将主启动类所在的包作为根路径。
- 执行扫描 -> 在这个路径及其子路径下,寻找所有被
@Component(及其衍生注解@Service,@Controller等) 标注的类。 - 实例化和注册 -> 将找到的类通过反射创建成实例(Bean),并放入 IoC 容器中。
- 依赖注入 -> 在创建 Bean 的过程中,如果发现
@Autowired注解,就从容器中找到匹配的 Bean 并注入进去,完成对象之间的“自动装配”。
如果没有 Spring Boot 会怎么样?
在没有 Spring Boot 的“远古” Spring MVC 时代,我们需要手动完成这一切:
- 在 XML 配置文件中明确开启组件扫描:
<context:component-scan base-package="com.myapp" /> - 或者在 Java 配置类中明确写上
@ComponentScan:@Configuration @ComponentScan("com.myapp") public class AppConfig { // ... 其他配置 }
Spring Boot 的伟大之处在于,它通过 @SpringBootApplication 这个小小的注解,将这些过去需要手动配置的内容,变成了一种自动的、符合约定的,极大地简化了开发者的工作。我们只需要遵守把类放在正确包下的约定,其他的一切都由框架自动完成。

被折叠的 条评论
为什么被折叠?



