Spring Bean 注入与容器管理:从“怎么交给容器”到“怎么被注入使用”的完整总结

Spring Bean 注入与容器管理:从"怎么交给容器"到"怎么被注入使用"的完整总结

面向学习复习:把 Spring 容器里最核心的三件事讲清楚

  1. Bean 如何注册进容器(交给 Spring 管理)
  2. Spring 容器如何管理 Bean(创建、增强、生命周期、销毁)
  3. 依赖注入如何发生(@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 类:

  1. 组件扫描(类上注解)
  2. @Bean(方法返回值)
  3. MyBatis Mapper(代理对象)
  4. 配置属性类(读取 yml)
  5. 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:默认是类名首字母小写
    DishServiceImpldishServiceImpl

  • @Bean:默认是方法名
    aliOssUtil()aliOssUtil

你也可以显式指定:

java 复制代码
@Bean("ossUtil")
public AliOssUtil aliOssUtil() { ... }

13. 为什么会出现 "Could not autowire. No beans found ..."?

本质原因:容器里没有对应 Bean

常见情况:

  1. 只有接口,没有实现类加 @Service
  2. 实现类不在扫描包范围
  3. 忘记加 @Component/@Service
  4. 类不是 Bean,却试图在其中 @Autowired
  5. 多模块依赖未引入导致类根本没被加载

排查思路:

  • 看实现类是否被扫描
  • 看注解是否存在
  • 看启动类包结构
  • 看 beanName 是否冲突/重复

14. 总结:从"注册"到"注入"的一条线串起来

text 复制代码
1) 注册阶段:把 BeanDefinition 放进容器(扫描/@Bean/@MapperScan/自动配置...)
2) 创建阶段:容器按需创建 Bean(实例化)
3) 注入阶段:容器解析依赖并注入(byType/byName)
4) 增强阶段:AOP/事务等通过后置处理器织入代理
5) 生命周期阶段:初始化回调 → 使用 → 容器关闭销毁

总结:

Bean 能被注入的前提是:它先通过某种方式注册进容器;Spring 创建 Bean 时会根据依赖关系完成注入,并在生命周期内统一管理初始化、增强与销毁。

相关推荐
javaIsGood_20 分钟前
Java基础面试题
java·开发语言
indexsunny22 分钟前
互联网大厂Java求职面试实战:基于电商场景的技术问答及解析
java·spring boot·redis·kafka·security·microservices·面试指导
Forget_85501 小时前
RHEL——LVS模式
java·开发语言·lvs
渣瓦攻城狮1 小时前
互联网大厂Java面试:从数据库连接池到分布式缓存及微服务
java·redis·spring cloud·微服务·hikaricp·数据库连接池·分布式缓存
罗超驿1 小时前
13.1 万字长文,深入解析--抽象类和接口
java·开发语言
A懿轩A1 小时前
【Java 基础编程】Java 面向对象进阶:static/final、抽象类、接口、单例模式
java·开发语言·单例模式
lifallen2 小时前
后缀数组 (Suffix Array)
java·数据结构·算法
逆境不可逃2 小时前
LeetCode 热题 100 之 76.最小覆盖子串
java·算法·leetcode·职场和发展·滑动窗口
I_LPL2 小时前
day35 代码随想录算法训练营 动态规划专题3
java·算法·动态规划·hot100·求职面试
百锦再2 小时前
Java中的日期时间API详解:从Date、Calendar到现代时间体系
java·开发语言·spring boot·struts·spring cloud·junit·kafka