步行cgn头像
关注

Spring Bean 生命周期的 7 步详解

Spring Bean 生命周期的 7 步详解

Bean 生命周期是 Spring 面试和源码阅读中绕不开的核心话题。相比 5 步版本,7 步版本更完整,把 Aware 接口回调和销毁阶段也纳入进来,能更准确地描述 Bean 从创建到销毁的全过程。

一、7 步总览

① 实例化 → ② 属性赋值 → ③ Aware 回调 → ④ 初始化前 → ⑤ 初始化 → ⑥ 初始化后 → ⑦ 销毁
步骤核心动作对应扩展点
1. 实例化调用构造方法创建对象InstantiationAwareBeanPostProcessor
2. 属性赋值依赖注入@Autowired@Value
3. Aware 回调获取容器内部资源BeanNameAwareApplicationContextAware
4. 初始化前前置增强postProcessBeforeInitialization
5. 初始化执行自定义初始化方法@PostConstructInitializingBeaninit-method
6. 初始化后后置增强,AOP 代理生成postProcessAfterInitialization
7. 销毁释放资源@PreDestroyDisposableBeandestroy-method

二、逐步拆解

第 1 步:实例化

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

@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 此时为 null
    
    public UserService() {
        System.out.println("1. 实例化,userDao = " + userDao);  // null
    }
}

这一步的扩展点是 InstantiationAwareBeanPostProcessor,它可以在实例化前后介入,但日常开发中很少直接实现这个接口。AOP 的代理通常不在这一步生成,而是在第 6 步。

第 2 步:属性赋值

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

@Autowired
private UserDao userDao;  // 此时被注入

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

如果存在循环依赖,Spring 通过三级缓存提前暴露对象的早期引用。这也是为什么字段注入能解决循环依赖,而构造器注入不能——构造器注入在第 1 步就要求依赖就绪,字段注入在第 2 步才处理。

第 3 步:Aware 回调

如果 Bean 实现了 Aware 系列接口,Spring 会回调对应方法,把容器内部资源传进来。这一步让 Bean 能感知自己在容器中的“身份”和“环境”。

Aware 接口回调方法获取的资源
BeanNameAwaresetBeanName(String)Bean 的名称
BeanFactoryAwaresetBeanFactory(BeanFactory)BeanFactory 实例
ApplicationContextAwaresetApplicationContext(ApplicationContext)ApplicationContext 实例
EnvironmentAwaresetEnvironment(Environment)Environment 实例
ResourceLoaderAwaresetResourceLoader(ResourceLoader)资源加载器
ApplicationEventPublisherAwaresetApplicationEventPublisher(...)事件发布器
@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");
    }
}

Aware 接口的调用顺序通常与接口在类中的声明顺序无关,而是由 AbstractAutowireCapableBeanFactory.invokeAwareMethods() 按固定顺序调用:BeanNameAwareBeanClassLoaderAwareBeanFactoryAwareApplicationContextAware 等更高级的 Aware 接口由 ApplicationContextAwareProcessor 处理,在 BeanPostProcessorpostProcessBeforeInitialization 阶段调用。

第 4 步:初始化前

BeanPostProcessorpostProcessBeforeInitialization 方法被调用。这是 Spring 留给开发者的扩展点,可以在初始化方法执行之前做额外处理。

@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 也不会被注册到容器中。

注意:ApplicationContextAware 等 Aware 接口的回调实际上也是在 ApplicationContextAwareProcessorpostProcessBeforeInitialization 中执行的,所以第 3 步和第 4 步在源码层面有重叠。为了便于理解,通常把 Aware 回调单独列为一步。

第 5 步:初始化

按固定顺序执行三种初始化方法:

  1. @PostConstruct 标注的方法
  2. InitializingBean.afterPropertiesSet()
  3. init-method@Bean(initMethod = "...")
@Component
public class MyBean implements InitializingBean {
    
    @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");
    }
}

执行顺序固定,不能调换。@PostConstructCommonAnnotationBeanPostProcessor 处理,afterPropertiesSetInitializingBean 接口触发,init-methodAbstractAutowireCapableBeanFactory 调用。

这一步完成后,Bean 的依赖和初始化都已完成,可以安全使用注入的依赖。

第 6 步:初始化后

BeanPostProcessorpostProcessAfterInitialization 方法被调用。AOP 代理就是在这个阶段生成的。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // Spring 的 AbstractAutoProxyCreator 在这里创建代理
    return bean;
}

如果 Bean 需要被 AOP 代理,容器中最终存放的是代理对象,而不是原始对象。这就是为什么同一个类内部方法调用不会触发 AOP——内部调用走的是原始对象,不是代理对象。

这一步完成后,Bean 被放入单例池(singletonObjects),可以被应用程序使用。

第 7 步:销毁

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

  1. @PreDestroy 标注的方法
  2. DisposableBean.destroy()
  3. destroy-method@Bean(destroyMethod = "...")
@Component
public class MyBean implements DisposableBean {
    
    @PreDestroy
    public void preDestroy() {
        System.out.println("7.1 @PreDestroy");
    }
    
    @Override
    public void destroy() {
        System.out.println("7.2 destroy");
    }
    
    public void customDestroy() {
        System.out.println("7.3 destroy-method");
    }
}

执行顺序与初始化阶段对应:@PreDestroydestroy()destroy-method

三、7 步完整示例

@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("7.1 @PreDestroy 执行");
    }
    
    @Override
    public void destroy() {
        System.out.println("7.2 destroy 执行");
    }
    
    public void customDestroy() {
        System.out.println("7.3 destroy-method 执行");
    }
}

配置:

@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleBean lifecycleBean() {
    return new LifecycleBean();
}

输出:

1. 实例化:构造方法执行
2. 属性赋值:userDao 被注入
3. BeanNameAware.setBeanName: lifecycleBean
4. 初始化前:postProcessBeforeInitialization
5.1 @PostConstruct 执行
5.2 afterPropertiesSet 执行
5.3 init-method 执行
6. 初始化后:postProcessAfterInitialization
(Bean 就绪)
... 应用运行 ...
7.1 @PreDestroy 执行
7.2 destroy 执行
7.3 destroy-method 执行

四、5 步 vs 7 步

对比5 步版本7 步版本
步骤实例化、属性赋值、初始化前、初始化、初始化后多了 Aware 回调和销毁
适用场景快速回答、日常面试完整理解、源码阅读
核心差异忽略 Aware 回调和销毁覆盖 Bean 完整生命周期

5 步版本把 Aware 回调归入初始化前阶段,销毁阶段单独讨论。7 步版本把 Aware 回调和销毁都显式列出来,描述更完整。面试时可以先答 5 步,面试官追问再展开到 7 步。

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

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

prototype 作用域的 Bean,第 7 步不会执行。Spring 只负责创建,不管理销毁。

六、常见追问

1. 为什么 @PostConstruct 能拿到注入的依赖?

因为它在第 5 步执行,而第 2 步已经完成了属性赋值。

2. AOP 代理在哪一步生成?

第 6 步,postProcessAfterInitialization。所以容器中存放的是代理对象。

3. 为什么内部方法调用不触发 AOP?

内部调用走的是原始对象,不是代理对象。代理对象只在容器中,外部调用才经过代理。

4. 构造方法中能用注入的依赖吗?

不能。第 1 步实例化时,第 2 步属性赋值还没开始。

5. Aware 回调在第几步?

第 3 步。BeanNameAwareBeanFactoryAware 等在 invokeAwareMethods() 中调用,ApplicationContextAware 等在第 4 步的 ApplicationContextAwareProcessor 中调用。为了便于理解,统一归为第 3 步。

七、总结

步骤动作关键扩展点
1. 实例化反射调用构造方法InstantiationAwareBeanPostProcessor
2. 属性赋值依赖注入@Autowired@Value
3. Aware 回调获取容器资源BeanNameAwareApplicationContextAware
4. 初始化前前置增强postProcessBeforeInitialization
5. 初始化执行初始化方法@PostConstructafterPropertiesSetinit-method
6. 初始化后后置增强,AOP 代理生成postProcessAfterInitialization
7. 销毁释放资源@PreDestroydestroydestroy-method

7 步版本比 5 步更完整,把 Aware 回调和销毁阶段都纳入进来。理解每一步的执行顺序和扩展点,就能解释 Spring 中大部分与 Bean 创建、代理、初始化、销毁相关的现象。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_51288065/article/details/166586426

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--