项目写得再多也没用!Spring Bean 的核心概念要是没懂,迟早踩坑

289 阅读5分钟

原文来自于:zha-ge.cn/java/129

项目写得再多也没用!Spring Bean 的核心概念要是没懂,迟早踩坑

有次带新人改项目,他拍着胸脯说:“我写过好几个 Spring 项目,Bean 我用得飞起。” 结果一问:“那 Bean 的生命周期有几个阶段?什么时候依赖注入?什么时候销毁?”——表情秒变成 🫠。

说实话,这种事我自己也踩过坑。Spring 的魔法 80% 都藏在 Bean 这俩字后面,你以为只是一个对象,其实是整个容器的灵魂。 项目写得再多,Bean 没整明白,迟早要在调试里原地爆炸


1. Bean 究竟是个啥?不是“new”出来的对象!

一句话总结:Bean 是由 Spring 容器创建、管理、装配和销毁的对象。

这意味着它不是你手动 new 出来的那种对象,而是被托管在 IoC 容器中,生命周期、依赖注入、作用域这些都交给容器来操心。

你要一个 UserService

@Service
public class UserService {
    public void register() { ... }
}

Spring 就在启动时扫描到它 → 实例化 → 放进 IoC 容器 → 以后你用的时候,直接拿已经准备好的 Bean 实例

@Autowired
private UserService userService;

你不管它是怎么 new 出来的、依赖怎么塞进去的、啥时候销毁的,这些都被 Spring 管了。这就是 Bean 的核心价值:解耦对象的创建和使用。


2. Bean 的生命周期:从出生到退休的一生

很多人知道“Spring 会帮我创建 Bean”,但你真知道它从出生到死亡有几步吗?这其实是面试最爱考的基础题之一👇:

  1. 实例化(Instantiation)

    • Spring 通过反射创建 Bean 实例(构造器注入就在这里搞定)。
  2. 依赖注入(Populate Properties)

    • 把其他 Bean 塞进来,比如用 @Autowired 注入。
  3. 调用 Aware 接口(可选)

    • 如果 Bean 实现了 BeanNameAwareApplicationContextAware 等,会在此时回调。
  4. BeanPostProcessor 前置处理

    • 比如自动代理(AOP)就在这里织入。
  5. 初始化(Initialization)

    • 调用 @PostConstructInitializingBean.afterPropertiesSet()
  6. BeanPostProcessor 后置处理

    • 后期增强处理完成。
  7. 就绪(Ready)

    • Bean 开始正式提供服务。
  8. 销毁(Destruction)

    • 容器关闭时调用 @PreDestroyDisposableBean.destroy()

👉 一句话记忆法:实例化 → 注入 → 感知 → 前置增强 → 初始化 → 后置增强 → 就绪 → 销毁

这 8 步你要是没概念,排查 Bean 初始化问题会特别痛苦。


3. 作用域:Bean 不止一种“活法”

默认情况下,Spring 的 Bean 是单例(singleton)的,但它还有多种作用域模式,搞清楚它们是理解 Bean 行为的关键。

作用域含义适用场景
singleton整个容器里只有一个实例(默认)大多数服务类
prototype每次注入都新建一个实例有状态对象
request每个 HTTP 请求一个实例(Web 环境)Web 请求级别的组件
session每个 Session 一个实例(Web 环境)用户会话数据
applicationServletContext 级别单例Web 全局状态共享

最容易踩坑的是 prototype:容器不会管理它的销毁,生命周期你得自己收尾。比如:

@Bean
@Scope("prototype")
public Task task() {
    return new Task();
}

这类 Bean 通常用在“短生命周期”或“临时状态”对象上,比如线程任务、策略对象等。


4. Bean 和普通对象的本质区别

这题是面试官最喜欢用来“筛选初中级”的问题之一👇:

对比项普通对象Spring Bean
创建new 手动创建容器反射实例化
生命周期由程序员控制由 IoC 容器管理
注入手动调用构造器/Setter自动依赖注入
配置写死在代码里可由配置文件/注解动态控制
管理程序员负责Spring 负责全生命周期管理

简单说,Bean 是容器里的“公民”,而你手动 new 出来的只是“野生对象”。


5. 常见坑点:Bean 搞不懂,项目迟早出事

🔴 坑 1:在容器外手动 new 对象 你以为只是 new 了一下,结果它的依赖全是 null,AOP 不生效,事务也没用。

解决:始终通过容器获取 Bean(@Autowired / ApplicationContext.getBean())。


🔴 坑 2:误把 Bean 当线程安全用 Spring 只保证单例 Bean 是唯一实例不保证线程安全。 如果它有可变状态,就得自己加锁或用原子类。


🔴 坑 3:prototype 用完不销毁 prototype Bean 的销毁容器不管,容易内存泄漏。

解决:使用完毕手动清理资源,或交给自定义工厂管理。


6. 面试官最爱的灵魂拷问(附答案)

Q:Spring Bean 默认是线程安全的吗? 👉 不是。singleton 只是单实例,不等于线程安全。有状态对象要自己保证并发安全。

Q:Bean 的生命周期有哪些阶段? 👉 实例化 → 依赖注入 → Aware → 前置处理 → 初始化 → 后置处理 → 就绪 → 销毁

Q:prototype Bean 和 singleton 的区别? 👉 前者每次注入新建实例,不受容器销毁管理;后者全局单例,由容器全程托管。

Q:Bean 和普通对象有何不同? 👉 Bean 由 IoC 容器管理生命周期、注入依赖、支持 AOP 等;普通对象一切都得自己来。


总结:Bean 不只是“对象”,而是 Spring 的灵魂

项目能跑不代表你理解了 Spring,Bean 是一切的起点:

  • 它不仅是“被托管的对象”,更是容器与业务代码的连接点;
  • 它的生命周期决定了依赖注入、AOP、事务能否正确工作;
  • 它的作用域影响着实例行为和线程安全;
  • 它的管理方式,决定了你的代码是“Spring 风格”还是“面向 new 风格”。

一句话记住:

“Bean 不是你 new 出来的对象,而是 Spring 世界的公民。你要理解它的一生,才能真正理解 Spring。”