如何通过注解声明一个 Bean?
最核心的注解是 @Component。它是一个通用的、万能的标记。
import org.springframework.stereotype.Component;
@Component // 嘿,Spring!这个类是一个组件,请帮我创建一个Bean。
public class MyGenericComponent {
public void doSomething() {
System.out.println("Doing something...");
}
}
为了让代码的意图更清晰,Spring 提供了几个继承自 @Component 的、具有特定语义的注解。它们在功能上与 @Component 几乎完全相同,但能更好地表达该组件在分层架构中的角色。
-
@Service: 用于标注业务逻辑层(Service Layer)的组件。import org.springframework.stereotype.Service; @Service // 这个组件是处理业务逻辑的 public class UserServiceImpl implements UserService { // ... } -
@Repository: 用于标注数据访问层/持久层(Repository/DAO Layer)的组件。- 额外功能: 除了标记为 Bean,
@Repository还能将特定于数据访问技术的异常(如 JDBC 的SQLException)转译为 Spring 统一的、与技术无关的DataAccessException异常体系。这使得你的业务层代码无需处理底层的特定异常。
import org.springframework.stereotype.Repository; @Repository // 这个组件是用来访问数据库的 public class MySqlUserRepository implements UserRepository { // ... } - 额外功能: 除了标记为 Bean,
-
@Controller/@RestController: 用于标注表现层/控制层(Presentation Layer)的组件,通常用于 Spring MVC 或 Spring WebFlux。import org.springframework.web.bind.annotation.RestController; @RestController // 这个组件是处理Web请求的API控制器 public class UserController { // ... } -
@Configuration: 用于标注配置类。配置类本身也是一个 Bean,它通常包含用@Bean注解的方法,用于以 Java 代码的方式定义其他的 Bean。import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; @Configuration // 这个类是专门用来定义Bean的 public class AppConfig { @Bean // 这个方法返回的对象也要作为一个Bean来管理 public MyCustomService myCustomService() { return new MyCustomService(); } }
总结:你只需要根据组件的角色,在类上加上 @Component、@Service、@Repository 或 @Controller 中的一个,就完成了 Bean 的声明。
Spring 是如何找到这些注解的?(Spring 做什么)
我们仅仅在类上加了注解,Spring 是如何知道要去扫描这些类并创建 Bean 的呢?答案是:组件扫描(Component Scanning)。
这个过程就像雷达扫描一样,Spring 会在你指定的“空域”(包路径)中进行扫描,寻找带有特定注解的类。
这个过程由 @ComponentScan 注解触发。
工作流程详解:
-
启动指令:
@ComponentScan
你必须在一个配置类(一个标注了@Configuration的类)上使用@ComponentScan来告诉 Spring:“请开始扫描!”。import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = "com.myapp.service") // 告诉Spring:请扫描这个包以及它的所有子包 public class AppConfig { // ... }basePackages: 明确指定要扫描的包路径。可以指定多个。- 默认行为: 如果不指定
basePackages,Spring 会默认从当前配置类所在的包开始扫描。这是 Spring Boot 的核心约定,@SpringBootApplication注解内部就包含了@ComponentScan,所以它会自动扫描主启动类所在的包及其所有子包。
-
扫描执行:
ClassPathBeanDefinitionScanner
当 Spring IoC 容器启动并加载AppConfig时,它会解析到@ComponentScan注解。然后,它会内部创建一个名为ClassPathBeanDefinitionScanner的扫描器。 -
遍历类路径
这个扫描器会像一个文件浏览器一样,根据你提供的basePackages,去文件系统(或 JAR 包)的类路径下,递归地查找所有.class文件。 -
读取元数据
对于找到的每一个.class文件,Spring 不会立即加载整个类到 JVM 中(这样效率太低)。它会使用一种称为 ASM 的字节码操作库,直接读取.class文件的二进制内容,从中解析出类的元数据,比如类名、接口、以及最重要的——类上的注解。 -
识别候选者
扫描器会检查每个类的元数据,看它是否被@Component或其衍生注解(@Service,@Repository等)所标记。 -
创建 BeanDefinition
如果一个类被识别为候选者,Spring 并不会立刻创建这个类的实例。相反,它会为这个类创建一个“Bean 的定义信息”对象,即BeanDefinition。
BeanDefinition就像一个菜谱,它描述了如何创建这个 Bean 的所有信息,包括:- Bean 的类名 (e.g.,
com.myapp.service.UserServiceImpl)。 - Bean 的作用域(Scope),默认是
singleton(单例)。 - 是否是懒加载(Lazy Init)。
- 依赖关系(比如它需要注入哪些其他的 Bean)。
- Bean 的类名 (e.g.,
-
注册 BeanDefinition
所有创建好的BeanDefinition都会被注册到一个名为BeanDefinitionRegistry的中央注册表中。至此,扫描阶段完成。容器现在已经“知道”了所有需要它管理的 Bean 的蓝图。 -
实例化 Bean
最后,在容器生命周期的后续阶段(通常是容器刷新完成时,或者第一次请求该 Bean 时),容器会根据BeanDefinitionRegistry中的“菜谱”(BeanDefinition),通过反射来创建 Bean 的实例,执行依赖注入,并完成初始化,最终将一个功能完备的 Bean 放入容器的单例缓存池中,以备后用。
流程总结
| 你做什么 (声明) | Spring 做什么 (发现和管理) |
|---|---|
1. 在你的类上添加 @Component 或其衍生注解 (@Service, @Repository 等)。 | 1. 启动时加载一个配置类 (@Configuration)。 |
2. 在配置类上添加 @ComponentScan,告诉 Spring 从哪里开始扫描。 | 2. 发现 @ComponentScan 注解,获取要扫描的包路径。 |
3. 使用扫描器在指定的类路径下查找所有 .class 文件。 | |
4. 高效读取每个 .class 文件的元数据(特别是注解)。 | |
5. 识别出被 @Component 等注解标记的类。 | |
6. 为每个识别出的类创建一个 BeanDefinition (Bean 的蓝图/菜谱)。 | |
7. 将所有 BeanDefinition 注册到容器的注册表中。 | |
8. 在需要时,根据 BeanDefinition 实例化、注入并初始化真正的 Bean 对象。 |
这个“注解声明 + 组件扫描”的机制是 Spring 框架实现自动化配置和依赖注入的基石,也是让 Spring Boot 能够实现“约定优于配置”的关键技术。

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



