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 时会根据依赖关系完成注入,并在生命周期内统一管理初始化、增强与销毁。

相关推荐
OxYGC16 分钟前
[AI工程] Spring AI 第十四篇:Agent 五种模式在 2.0 里怎么写
java·spring·ai·ai编程
code2cat17 分钟前
【随笔】从聊天到调用工具:理解MCP在AI应用中的位置
java·人工智能
2501_9339232525 分钟前
Spring Bean作用域揭秘:单例、原型、请求与会话的区别
java·后端·spring·java-ee
学编程就要猛43 分钟前
基于Spring AI 的智能聊天机器人
java·spring ai·chat robot
修炼室1 小时前
Java开发工程师笔试经验贴【高频知识】
java
木井巳1 小时前
【记忆化搜索】最长递增子序列
java·算法·leetcode·深度优先·剪枝·推荐算法
杨杨杨大侠1 小时前
知识库已经有了,Java 程序员还要做什么?Spring AI RAG 实战
java·openai·ai编程
伊信1 小时前
Spring AI 基础学习与应用
java·后端
夜郎king1 小时前
基于 Java + Playwright 实现网站自动访问与数据采集(以腾讯云开发者社区为例)
java·playwright·网页数据获取
Anastasiozzzz1 小时前
重新定义 Agent 基建:Redis 在现代 AI 与智能体系统中的工程实践
java·人工智能·redis·ai