Spring Bean 注入与容器管理:从"怎么交给容器"到"怎么被注入使用"的完整总结
面向学习复习:把 Spring 容器里最核心的三件事讲清楚
- Bean 如何注册进容器(交给 Spring 管理)
- Spring 容器如何管理 Bean(创建、增强、生命周期、销毁)
- 依赖注入如何发生(@Autowired / 构造器注入 / @Resource 等)
1. Spring 容器到底是什么?它管理什么?
1.1 Spring 容器的本质
Spring 容器可以理解为一个"对象工厂 + 对象仓库 + 生命周期管理器"。
它内部维护一个很关键的结构:
- BeanDefinition(Bean 的定义信息):类名、作用域、依赖关系、初始化/销毁方法等
- SingletonObjects(单例池):已经创建完成的单例对象缓存(key=beanName,value=bean实例)
1.2 容器管理的对象是谁?
容器管理的对象叫 Bean。
"交给容器管理"意味着:
- 对象由 Spring 创建(而不是你手动 new)
- 依赖由 Spring 注入
- AOP/事务等增强由 Spring 织入
- 初始化/销毁由 Spring 统一回调
2. Bean 是怎么"交给 Spring 容器管理"的?
本质上只有一句话:
让 Spring 在启动时"知道这个 BeanDefinition",并在需要时创建出实例。
常见注册方式分为 5 类:
- 组件扫描(类上注解)
@Bean(方法返回值)- MyBatis Mapper(代理对象)
- 配置属性类(读取 yml)
- Spring Boot 自动配置(starter)
下面展开。
3. 组件扫描注册:@Component/@Service/@Controller/@RestController/@Repository/@Aspect
3.1 怎么注册?
Spring 启动时会"扫描包路径",发现被标注的类,生成 BeanDefinition。
扫描范围通常由主启动类决定:
@SpringBootApplication所在包及子包会被默认扫描- 也可通过
@ComponentScan手动指定
3.2 关键过程(记流程)
text
启动 → 扫描包 → 发现带组件注解的类 → 为类创建 BeanDefinition → 注册到容器
3.3 常用注解与语义
| 注解 | 语义 | 常见层 |
|---|---|---|
| @Component | 通用组件 | 工具类/通用类 |
| @Service | 业务组件 | Service |
| @Repository | 持久层组件 | DAO |
| @Controller / @RestController | 控制器 | Controller |
| @Aspect + @Component | 切面组件 | AOP |
这些注解本质都属于组件扫描机制,只是语义不同。
4. @Bean 显式注册:你 new 对象,Spring 管理它
4.1 为什么要用 @Bean?
适用场景:
- 第三方类(不能加 @Component)
- 构造过程复杂(要传配置、组合对象)
- 需要创建多个不同实例
4.2 @Bean 的注册过程
text
启动 → 创建@Configuration类 → 执行@Bean方法 → 拿到返回对象 → 注册为 Bean
4.3 示例(典型:OSS/JWT 工具类)
java
@Configuration
public class OssConfig {
@Bean
public AliOssUtil aliOssUtil(OssProperties props) {
return new AliOssUtil(props.getEndpoint(), props.getAccessKeyId(),
props.getAccessKeySecret(), props.getBucketName());
}
}
5. MyBatis Mapper 注册:接口也能成为 Bean(代理)
Mapper 是接口,没有实现类,但能注入的原因是:
MyBatis 在启动时扫描 Mapper 接口,为每个接口创建一个"代理对象",并注册为 Bean。
注册方式:
@Mapper标注接口- 或
@MapperScan("com.sky.mapper")扫描整个包(推荐)
结果:
@Autowired DishMapper dishMapper;能注入,因为容器里有 Mapper 代理对象。
6. 配置属性类注册:@ConfigurationProperties
目的:把 yml 配置变成对象,并放进容器。
两种方式:
6.1 @Component + @ConfigurationProperties
java
@Component
@ConfigurationProperties(prefix="aliyun.oss")
public class OssProperties { ... }
6.2 @EnableConfigurationProperties
java
@EnableConfigurationProperties(OssProperties.class)
@Configuration
public class OssConfig { }
这类 Bean 通常是"配置 Bean",用于给其他 Bean 提供参数。
7. Spring Boot 自动配置:你没写 Bean,但容器里有
很多能力来自 starter:
- MVC
- Jackson
- MultipartFile 上传
- Tomcat
本质:Spring Boot 的 AutoConfiguration 根据"条件注解"按需注册 Bean。
学习阶段只需记结论:
starter 引入依赖 → 自动注册一批基础设施 Bean。
8. Spring 容器是怎么"管理 Bean 生命周期"的?
8.1 单例 Bean(默认)的完整生命周期(重点)
text
1) 读取 BeanDefinition
2) 实例化(new / 反射创建)
3) 属性填充(依赖注入)
4) Aware 回调(可选)
5) BeanPostProcessor 前置处理
6) 初始化:@PostConstruct / InitializingBean / init-method
7) Bean 可用(进入单例池)
8) 容器关闭:@PreDestroy / DisposableBean / destroy-method
8.2 初始化/销毁常用方式
- 初始化:
@PostConstruct - 销毁:
@PreDestroy - 或在
@Bean(initMethod="...", destroyMethod="...")
9. "注入"到底是什么?Spring 是怎么把依赖塞进去的?
依赖注入(DI)的核心:
当 Spring 创建一个 Bean 时,会查它依赖哪些其他 Bean,并把这些依赖自动填充进去。
注入发生的时机:生命周期第 3 步:属性填充(依赖注入)
10. 注入方式详解
10.1 构造器注入
java
@Service
public class DishServiceImpl implements DishService {
private final DishMapper dishMapper;
public DishServiceImpl(DishMapper dishMapper) {
this.dishMapper = dishMapper;
}
}
特点:
- 依赖不可变(final)
- 更易测试
- 避免空指针
10.2 字段注入
java
@Autowired
private DishMapper dishMapper;
特点:
- 写法简单
- 但不利于单元测试、依赖不可见
10.3 Setter 注入
java
@Autowired
public void setDishMapper(DishMapper dishMapper) { ... }
适合可选依赖。
11. @Autowired 注入规则
11.1 默认按类型注入(byType)
@Autowired DishService dishService;
- 容器里找类型为 DishService 的 Bean
11.2 如果同类型有多个 Bean,会报错
解决方法:
@Qualifier("beanName")指定名称- 或
@Primary指定默认
示例:
java
@Autowired
@Qualifier("dishServiceImpl")
private DishService dishService;
12. Bean 名称(beanName)是怎么来的?
-
@Component:默认是类名首字母小写
DishServiceImpl→dishServiceImpl -
@Bean:默认是方法名
aliOssUtil()→aliOssUtil
你也可以显式指定:
java
@Bean("ossUtil")
public AliOssUtil aliOssUtil() { ... }
13. 为什么会出现 "Could not autowire. No beans found ..."?
本质原因:容器里没有对应 Bean。
常见情况:
- 只有接口,没有实现类加
@Service - 实现类不在扫描包范围
- 忘记加
@Component/@Service - 类不是 Bean,却试图在其中 @Autowired
- 多模块依赖未引入导致类根本没被加载
排查思路:
- 看实现类是否被扫描
- 看注解是否存在
- 看启动类包结构
- 看 beanName 是否冲突/重复
14. 总结:从"注册"到"注入"的一条线串起来
text
1) 注册阶段:把 BeanDefinition 放进容器(扫描/@Bean/@MapperScan/自动配置...)
2) 创建阶段:容器按需创建 Bean(实例化)
3) 注入阶段:容器解析依赖并注入(byType/byName)
4) 增强阶段:AOP/事务等通过后置处理器织入代理
5) 生命周期阶段:初始化回调 → 使用 → 容器关闭销毁
总结:
Bean 能被注入的前提是:它先通过某种方式注册进容器;Spring 创建 Bean 时会根据依赖关系完成注入,并在生命周期内统一管理初始化、增强与销毁。