Spring Bean生命周期

🍳 Spring Bean 生命周期全解析:用“做红烧肉”讲透每一个阶段

作者:赫英年
关键词:Spring Bean 生命周期、依赖注入、BeanPostProcessor、@PostConstruct、AOP 原理


🔥 引言:做一道“红烧肉”,理解一个 Spring Bean

在 Spring 框架中,Bean 的生命周期是核心机制之一。
今天我们用一道“家常红烧肉”来类比整个流程,从买肉到洗碗,完整还原 Spring 容器是如何管理一个 Bean 的。


🧩 整体比喻回顾:做一道“红烧肉” = 创建一个 Spring Bean

生活阶段Spring 阶段代码演示
1. 买肉实例化(Instantiation)new BraisedPork()
2. 腌肉属性赋值(Dependency Injection)@Autowired 酱油、料酒
3. 切肉前看看初始化前(postProcessBeforeInitialization)检查/替换对象
4. 正式开火初始化(@PostConstruct / init-method焯水、炒糖色
5. 上锅前再检查初始化后(postProcessAfterInitialization)加“代理”(如事务)
6. 上菜使用 Beanservice.cook()
7. 洗碗销毁(@PreDestroy / destroy-method关火、洗锅

🧱 1. 阶段一:买肉 —— 实例化(Instantiation)

比喻:去市场买一块五花肉 → 创建对象实例。

// BraisedPork.java
public class BraisedPork {

    private String name;
    private SoySauce soySauce;  // 酱油(依赖)
    private CookingOil oil;     // 食用油(依赖)

    // 无参构造:相当于“买肉”
    public BraisedPork() {
        System.out.println("🥩 1. 买肉:BraisedPork 实例化了!new 出来了!");
    }

    // setter 用于注入依赖(腌肉)
    public void setName(String name) {
        this.name = name;
    }

    public void setSoySauce(SoySauce soySauce) {
        this.soySauce = soySauce;
    }

    public void setOil(CookingOil oil) {
        this.oil = oil;
    }

    public void cook() {
        System.out.println("🍳 正在烹饪:" + name + ",用的酱油是:" + soySauce.getType());
    }
}

✅ 实例化就是 new BraisedPork(),Spring 容器负责调用构造函数。


🧂 2. 阶段二:腌肉 —— 属性赋值(Populate Properties)

比喻:用酱油、料酒腌肉 → 注入依赖。

// SoySauce.java
public class SoySauce {
    private String type;

    public SoySauce(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

// CookingOil.java
public class CookingOil {
    private String brand;

    public CookingOil(String brand) {
        this.brand = brand;
    }
}
// AppConfig.java
import org.springframework.context.annotation.*;

@Configuration
public class AppConfig {

    @Bean
    public SoySauce soySauce() {
        return new SoySauce("老抽");
    }

    @Bean
    public CookingOil cookingOil() {
        return new CookingOil("金龙鱼");
    }

    @Bean
    public BraisedPork braisedPork() {
        BraisedPork pork = new BraisedPork();
        pork.setName("家常红烧肉");  // 这一步就是“腌肉”
        return pork;
    }
}

✅ Spring 会自动把 soySauce()cookingOil() 注入到 BraisedPork 中(通过 setter 或字段注入)。


🔍 为什么能自动注入 soySauce() 和 cookingOil()?—— Spring 容器的“自动装配”机制

这段代码:

@Bean
public BraisedPork braisedPork() {
    BraisedPork pork = new BraisedPork();
    pork.setName("家常红烧肉");
    return pork;
}

看起来确实没有注入 SoySauceCookingOil,但 Spring 会在你返回这个对象之后,自动完成依赖注入!

🔧 关键机制:属性注入发生在 @Bean 方法返回之后

Spring 的流程是这样的:

  1. 调用你的 braisedPork() 方法,得到一个 BraisedPork 实例(此时只有 name 被设置)。
  2. Spring 拿到这个实例后,扫描它的字段或 setter 方法
  3. 发现它有 setSoySauce(SoySauce) 和 setOil(CookingOil) 方法(或 @Autowired 字段)。
  4. Spring 去容器里找类型匹配的 Bean(SoySauce 和 CookingOil)。
  5. 找到后,自动调用 pork.setSoySauce(...) 和 pork.setOil(...)
  6. 最终放入容器的 BraisedPork 是已经被注入好依赖的完整对象

✅ 举个更直白的例子

假设 Spring 内部是这样工作的(简化版):

// 第一步:调用你的 @Bean 方法
BraisedPork pork = appConfig.braisedPork(); 
// 此时 pork.name = "家常红烧肉",但 soySauce = null, oil = null

// 第二步:Spring 自动注入依赖
SoySauce sauce = appConfig.soySauce();     // 从容器获取
CookingOil oil = appConfig.cookingOil();   // 从容器获取

pork.setSoySauce(sauce);  // Spring 自动调用
pork.setOil(oil);         // Spring 自动调用

// 第三步:这个 pork 才真正放入 Spring 容器
container.put("braisedPork", pork);

所以,你不需要手动写 setSoySauce(),Spring 会帮你补上!


🔍 3. 阶段三:切肉前看看 —— 初始化前(postProcessBeforeInitialization

比喻:你正要切肉,发现肉太肥,决定换成瘦肉 → 可检查或替换 Bean。

// MyBeanPostProcessor.java
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("🔪 3. 切肉前检查:Bean '" + beanName + "' 即将初始化...");

        // 检查是不是红烧肉
        if (bean instanceof BraisedPork) {
            BraisedPork pork = (BraisedPork) bean;
            System.out.println("🔍 发现红烧肉,当前名字:" + pork.getClass().getSimpleName());

            // 可以修改它
            // 注意:
            // 1. 如果 name 是在 @Bean 方法中通过 setName() 手动设置的 → 此时已赋值,可安全修改
            // 2. 如果 name 是通过 @Value("${...}") 注入的 → 此时可能还未注入(@Value 处理时机较晚)
            //    在 postProcessBeforeInitialization 中读取或修改,可能拿到 null 或默认值
            //    改了也白改,因为后续还会被 @Value 覆盖        }

        // 甚至可以替换整个对象(高级玩法)
        /*
        if ("braisedPork".equals(beanName)) {
            System.out.println("⚠️  强行替换了红烧肉!");
            return new BraisedPork() {{
                setName("被替换的红烧肉");
            }};
        }
        */

        return bean; // 继续使用原对象
    }
}

✅ 这个方法在 @PostConstruct 之前调用,你可以:

  • 打日志
  • 做安全检查
  • 返回一个包装对象(如代理)
  • 极端情况下替换整个 Bean(不推荐)

🔥 4. 阶段四:正式开火 —— 初始化(Initialization)

比喻:焯水、炒糖色、加香料 → 准备工作。

// 在 BraisedPork.java 中添加:

import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.InitializingBean;

public class BraisedPork implements InitializingBean {
    // ... 其他代码

    @PostConstruct
    public void startFire() {
        System.out.println("🔥 4.1 @PostConstruct:开火!开始炒糖色...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("🔥 4.2 afterPropertiesSet:加八角、桂皮,调料下锅!");
    }

    public void customInit() {
        System.out.println("🔥 4.3 customInit:大火收汁,准备出锅!");
    }
}
// 修改 AppConfig.java 中的 Bean 定义:
@Bean(initMethod = "customInit")
public BraisedPork braisedPork() {
    BraisedPork pork = new BraisedPork();
    pork.setName("家常红烧肉");
    return pork;
}

✅ 执行顺序:

  1. @PostConstruct
  2. afterPropertiesSet()
  3. init-method

👨‍🍳 5. 阶段五:上锅前再检查 —— 初始化后(postProcessAfterInitialization

比喻:菜快好了,你给它加个“防伪标签”或“拍照” → AOP 代理生成。

// 继续在 MyBeanPostProcessor.java 中:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    System.out.println("👨‍🍳 5. 上锅前检查:Bean '" + beanName + "' 已初始化完成!");

    if (bean instanceof BraisedPork) {
        System.out.println("📸 给红烧肉加个‘美食滤镜’(AOP 代理)!");
        
        // 真实场景:Spring AOP 就在这里生成代理对象
        // 比如 @Transactional、@Cacheable 都是这么加的
    }

    return bean; // 实际上可以返回一个代理对象
}

✅ 这是 AOP 的核心阶段!Spring 在这里给你的 Bean “包一层”,比如加事务控制。


🍽️ 6. 阶段六:上菜 —— 使用 Bean

// App.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

        // 上菜!
        BraisedPork pork = ctx.getBean(BraisedPork.class);
        System.out.println("🍽️  6. 上菜啦!");
        pork.cook();

        // 关闭容器(触发销毁)
        ((AnnotationConfigApplicationContext) ctx).close();
    }
}

🧽 7. 阶段七:洗碗 —— 销毁(Destruction)

比喻:关火、洗锅、收拾厨房 → 释放资源。

// 在 BraisedPork.java 中添加:

import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.DisposableBean;

public class BraisedPork implements InitializingBean, DisposableBean {
    // ... 其他代码

    @PreDestroy
    public void turnOffFire() {
        System.out.println("🧽 7.1 @PreDestroy:关火!");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("🧽 7.2 DisposableBean.destroy:洗锅!");
    }

    public void customDestroy() {
        System.out.println("🧽 7.3 customDestroy:擦灶台,打扫厨房!");
    }
}
// 修改 AppConfig.java
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public BraisedPork braisedPork() {
    // ...
}

✅ 执行顺序:

  1. @PreDestroy
  2. destroy()
  3. destroy-method

🏁 运行结果(控制台输出)

深色版本🥩 1. 买肉:BraisedPork 实例化了!new 出来了!
🔪 3. 切肉前检查:Bean 'braisedPork' 即将初始化...
🔥 4.1 @PostConstruct:开火!开始炒糖色...
🔥 4.2 afterPropertiesSet:加八角、桂皮,调料下锅!
🔥 4.3 customInit:大火收汁,准备出锅!
👨‍🍳 5. 上锅前检查:Bean 'braisedPork' 已初始化完成!
📸 给红烧肉加个‘美食滤镜’(AOP 代理)!
🍽️  6. 上菜啦!
🍳 正在烹饪:家常红烧肉,用的酱油是:老抽
🧽 7.1 @PreDestroy:关火!
🧽 7.2 DisposableBean.destroy:洗锅!
🧽 7.3 customDestroy:擦灶台,打扫厨房!

✅ 总结:代码 + 比喻 对照表

阶段比喻代码关键点
1. 实例化买肉new BraisedPork()
2. 属性赋值腌肉@Autowired、setter
3. 初始化前切肉前检查BeanPostProcessor.postProcessBeforeInitialization
4. 初始化开火做菜@PostConstructafterPropertiesSetinit-method
5. 初始化后加滤镜/包装BeanPostProcessor.postProcessAfterInitialization(AOP)
6. 使用上菜getBean().cook()
7. 销毁洗碗@PreDestroydestroydestroy-method
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇