Spring Bean 的生命周期详解

Spring Bean 的生命周期详解

一、什么是 Bean 的生命周期?

Bean 的生命周期是指从 Spring 容器创建 Bean 实例开始,到最终销毁 Bean 的整个过程。理解这个过程,能帮你搞清楚很多问题:为什么 @PostConstruct 能在依赖注入完成后执行、AOP 代理是在哪个阶段生成的、@PreDestroy 为什么有时候不生效。

二、完整生命周期流程

Spring Bean 的生命周期可以概括为四个大阶段:实例化 → 属性填充 → 初始化 → 销毁。但在这四个阶段之间,穿插着多个扩展点。

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                     Bean 生命周期完整流程                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ① 实例化(Instantiation)                                      │
│     └── 通过反射调用构造方法创建对象                            │
│                                                                 │
│  ② 属性填充(Populate)                                         │
│     └── 依赖注入(@Autowired、@Value)                          │
│                                                                 │
│  ③ Aware 接口回调                                               │
│     ├── BeanNameAware.setBeanName()                             │
│     ├── BeanFactoryAware.setBeanFactory()                       │
│     ├── ApplicationContextAware.setApplicationContext()         │
│     └── 其他 Aware 接口                                         │
│                                                                 │
│  ④ BeanPostProcessor.postProcessBeforeInitialization()          │
│     └── 初始化前增强                                            │
│                                                                 │
│  ⑤ 初始化(Initialization)                                     │
│     ├── @PostConstruct 标注的方法                               │
│     ├── InitializingBean.afterPropertiesSet()                   │
│     └── init-method 或 @Bean(initMethod = "...")                │
│                                                                 │
│  ⑥ BeanPostProcessor.postProcessAfterInitialization()           │
│     └── 初始化后增强(AOP 代理在此生成)                        │
│                                                                 │
│  ⑦ Bean 就绪,可以使用                                          │
│                                                                 │
│  ⑧ 容器关闭,销毁 Bean                                          │
│     ├── @PreDestroy 标注的方法                                  │
│     ├── DisposableBean.destroy()                                │
│     └── destroy-method 或 @Bean(destroyMethod = "...")          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

三、各阶段详解

3.1 实例化(Instantiation)

Spring 通过反射调用构造方法创建 Bean 实例。如果使用构造器注入,依赖在此时作为参数传入。如果使用字段注入,此时对象已创建,但依赖字段还是 null

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 此时还是 null
    
    public UserService() {
        System.out.println("1. 构造方法执行,userDao = " + userDao);  // null
    }
}

3.2 属性填充(Populate)

Spring 扫描 Bean 中标注了 @Autowired@Value@Resource 的字段和方法,从容器中查找匹配的依赖并注入。

java 复制代码
@Autowired
private UserDao userDao;  // 此时被注入

@Value("${app.name}")
private String appName;   // 此时被注入

如果依赖是循环依赖,Spring 通过三级缓存提前暴露对象的早期引用,让依赖方先拿到引用。

3.3 Aware 接口回调

如果 Bean 实现了 Aware 系列接口,Spring 会调用对应的方法,让 Bean 获取容器内部的一些资源。

Aware 接口 回调方法 获取的资源
BeanNameAware setBeanName(String) Bean 的名称
BeanFactoryAware setBeanFactory(BeanFactory) BeanFactory 实例
ApplicationContextAware setApplicationContext(ApplicationContext) ApplicationContext 实例
EnvironmentAware setEnvironment(Environment) Environment 实例
ResourceLoaderAware setResourceLoader(ResourceLoader) 资源加载器
ApplicationEventPublisherAware setApplicationEventPublisher(...) 事件发布器
java 复制代码
@Component
public class MyBean implements BeanNameAware, ApplicationContextAware {
    
    @Override
    public void setBeanName(String name) {
        System.out.println("3. Bean 名称: " + name);
    }
    
    @Override
    public void setApplicationContext(ApplicationContext context) {
        System.out.println("3. 获取到 ApplicationContext");
    }
}

3.4 BeanPostProcessor 前置处理

BeanPostProcessor 是 Spring 最重要的扩展点之一。它的 postProcessBeforeInitialization 方法会在所有初始化方法执行之前被调用。

java 复制代码
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("4. 初始化前: " + beanName);
        return bean;  // 返回 null 则不会继续后续处理
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("6. 初始化后: " + beanName);
        return bean;
    }
}

如果返回 null,Spring 不会继续执行后续的初始化流程,Bean 也不会被注册到容器中。

3.5 初始化(Initialization)

初始化阶段按以下顺序执行:

1. @PostConstruct 标注的方法

java 复制代码
@PostConstruct
public void init() {
    System.out.println("5.1 @PostConstruct 执行");
}

2. InitializingBean.afterPropertiesSet()

java 复制代码
@Component
public class MyBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        System.out.println("5.2 afterPropertiesSet 执行");
    }
}

3. init-method@Bean(initMethod = "...")

java 复制代码
@Bean(initMethod = "customInit")
public MyBean myBean() {
    return new MyBean();
}

// MyBean 中定义 customInit 方法
public void customInit() {
    System.out.println("5.3 init-method 执行");
}

执行顺序:@PostConstructafterPropertiesSet()init-method

3.6 BeanPostProcessor 后置处理

postProcessAfterInitialization 在所有初始化方法执行之后被调用。AOP 代理就是在这个阶段生成的。

java 复制代码
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // 如果 Bean 需要被 AOP 代理,这里返回代理对象
    // Spring 的 AbstractAutoProxyCreator 就是在这里创建代理的
    return bean;
}

如果 Bean 被 AOP 代理,容器中最终存放的是代理对象,而不是原始对象。这也解释了为什么 @Transactional 注解的方法必须通过代理调用才生效。

3.7 Bean 就绪

经过以上所有阶段后,Bean 被放入单例池(singletonObjects),可以被应用程序使用。

3.8 销毁(Destruction)

容器关闭时,Bean 的销毁流程按以下顺序执行:

1. @PreDestroy 标注的方法

java 复制代码
@PreDestroy
public void preDestroy() {
    System.out.println("8.1 @PreDestroy 执行");
}

2. DisposableBean.destroy()

java 复制代码
@Component
public class MyBean implements DisposableBean {
    @Override
    public void destroy() {
        System.out.println("8.2 destroy 执行");
    }
}

3. destroy-method@Bean(destroyMethod = "...")

java 复制代码
@Bean(destroyMethod = "customDestroy")
public MyBean myBean() {
    return new MyBean();
}

执行顺序:@PreDestroydestroy()destroy-method

四、完整示例

java 复制代码
@Component
public class LifecycleBean implements BeanNameAware, InitializingBean, DisposableBean {
    
    @Autowired
    private UserDao userDao;
    
    public LifecycleBean() {
        System.out.println("1. 构造方法执行");
    }
    
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware.setBeanName: " + name);
    }
    
    @PostConstruct
    public void postConstruct() {
        System.out.println("5.1 @PostConstruct 执行");
    }
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("5.2 afterPropertiesSet 执行");
    }
    
    public void customInit() {
        System.out.println("5.3 init-method 执行");
    }
    
    @PreDestroy
    public void preDestroy() {
        System.out.println("8.1 @PreDestroy 执行");
    }
    
    @Override
    public void destroy() {
        System.out.println("8.2 destroy 执行");
    }
    
    public void customDestroy() {
        System.out.println("8.3 destroy-method 执行");
    }
}

配置:

java 复制代码
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleBean lifecycleBean() {
    return new LifecycleBean();
}

输出:

复制代码
1. 构造方法执行
2. 属性填充(依赖注入)
3. BeanNameAware.setBeanName: lifecycleBean
4. BeanPostProcessor.postProcessBeforeInitialization
5.1 @PostConstruct 执行
5.2 afterPropertiesSet 执行
5.3 init-method 执行
6. BeanPostProcessor.postProcessAfterInitialization
7. Bean 就绪
... 应用运行 ...
8.1 @PreDestroy 执行
8.2 destroy 执行
8.3 destroy-method 执行

五、作用域对生命周期的影响

作用域 生命周期
singleton 容器启动时创建,容器关闭时销毁
prototype 每次获取时创建,Spring 不管理销毁(需要调用方自行处理)
request 每次 HTTP 请求创建,请求结束时销毁
session 每个会话创建,会话失效时销毁
application 整个 ServletContext 生命周期

prototype 作用域的 Bean,Spring 只负责创建,不负责销毁。@PreDestroyDisposableBean 不会生效。

六、生命周期中的扩展点

扩展点 类型 作用
BeanPostProcessor 接口 在初始化前后增强 Bean
InstantiationAwareBeanPostProcessor 接口 在实例化前后增强 Bean
@PostConstruct 注解 初始化方法
InitializingBean 接口 初始化方法
init-method XML/注解 初始化方法
@PreDestroy 注解 销毁方法
DisposableBean 接口 销毁方法
destroy-method XML/注解 销毁方法

七、常见问题

1. @PostConstruct 在依赖注入完成后才执行

这个顺序保证了 @PostConstruct 方法中可以直接使用注入的依赖,不会出现空指针。

java 复制代码
@PostConstruct
public void init() {
    userDao.count();  // 安全,userDao 已经被注入
}

2. AOP 代理在哪个阶段生成?

AOP 代理在 BeanPostProcessor.postProcessAfterInitialization() 中生成。所以如果 Bean 被 AOP 代理,容器中存放的是代理对象。这也解释了为什么同一个类内部方法调用不会触发 AOP------因为内部调用走的是原始对象,不是代理对象。

3. 构造方法中为什么不能使用注入的依赖?

构造方法执行时,属性填充还没开始。所以构造方法中 userDao 还是 null。如果需要在构造方法中使用依赖,应使用构造器注入。

4. prototype Bean 的销毁方法为什么不执行?

Spring 只负责创建 prototype Bean,创建完成后就不再管理它的生命周期。销毁方法需要调用方手动调用,或者通过 DisposableBeanAdapter 自行处理。

5. BeanPostProcessor 为什么能拦截所有 Bean?

BeanPostProcessor 本身也是一个 Bean,但它的注册时机非常早(在 refresh()registerBeanPostProcessors() 阶段)。Spring 在创建任何 Bean 时,都会先获取所有已注册的 BeanPostProcessor,依次调用它们的后置处理方法。

八、总结

Bean 的生命周期可以概括为四个核心阶段:实例化 → 属性填充 → 初始化 → 销毁。在这四个阶段之间,Spring 提供了多个扩展点,让开发者可以在合适的时机插入自定义逻辑。

阶段 核心操作 常用扩展点
实例化 反射调用构造方法 InstantiationAwareBeanPostProcessor
属性填充 依赖注入 @Autowired@Value
Aware 回调 获取容器资源 BeanNameAwareApplicationContextAware
初始化前 BeanPostProcessor 前置 postProcessBeforeInitialization
初始化 执行初始化逻辑 @PostConstructInitializingBeaninit-method
初始化后 BeanPostProcessor 后置 postProcessAfterInitialization(AOP 代理在此生成)
使用 Bean 就绪 ---
销毁 释放资源 @PreDestroyDisposableBeandestroy-method

理解 Bean 生命周期,能帮你解释很多 Spring 中的"奇怪现象":为什么 @PostConstruct 能拿到注入的依赖、为什么 AOP 代理的方法调用有"自调用失效"问题、为什么 prototype Bean 的销毁方法不生效。掌握生命周期,就掌握了 Spring 容器管理的底层逻辑。

相关推荐
ps酷教程1 小时前
数据权限DataPersmission
java·学习
驭渊的小故事1 小时前
Java 项目-jckson序列化工具
java·开发语言
君顾11 小时前
上海24小时自助健身房系统开发实战指南:从架构设计到功能实现
java·开发语言·健身房
爱勇宝1 小时前
用了一个月 WorkBuddy,聊聊我的真实感受
前端·后端·程序员
用户3721574261352 小时前
Java 拆分 Word 文档教程:按页、分页符和分节符拆分
java
liyinchi19882 小时前
微信小程序支付遇到“由于小程序违规,支付功能暂时无法使用” 解决办法
java·微信小程序·go
横木沉2 小时前
IntelliJ IDEA 无法识别 Git:Git is not installed 问题解决
java·git·github·intellij-idea