Spring Bean 生命周期全解析

候选人小周在面试蚂蚁时,面试官问了一个经典问题:

"Spring Bean 的生命周期说一下。"

小周答:"先实例化,然后设置属性,再初始化,最后销毁。"

面试官追问:"具体有哪些回调接口?Aware 接口什么时候执行?BeanPostProcessor 在哪个阶段工作?"

小周支支吾吾,说了五六个节点就开始卡壳了。

面试官继续追问:"InitializingBean 和 init-method 谁先执行?BeanPostProcessor 和 InitializingBean 的执行顺序是什么?"

小周彻底答不上来。

【面试官心理】 Bean 生命周期是 Spring 面试中的高频考点。90%的候选人能说出大概流程,但能完整说出12个节点、搞清Aware接口顺序、说清 BeanPostProcessor 和 InitializingBean 关系的不到20%。这道题是区分"看过几篇博客"和"真正研究过源码"的分水岭。

一、Bean 生命周期12步全景 🔴

1.1 完整流程图

graph TD
    A[1. 实例化<br/>instantiateBean] --> B[2. 实例化后处理<br/>addSingletonFactory]
    B --> C[3. 属性填充<br/>populateBean]
    C --> D[4. Aware 接口回调<br/>invokeAwareMethods]
    D --> E[5. BeanPostProcessor前置<br/>postProcessBeforeInitialization]
    E --> F[6. 初始化<br/>invokeInitMethods]
    F --> G[7. BeanPostProcessor后置<br/>postProcessAfterInitialization]
    G --> H[8. 使用]
    H --> I[9. 销毁前回调<br/>destroy()开始]
    I --> J[10. 销毁前自定义<br/>destroyMethod]
    J --> K[11. 销毁<br/>DisposableBean.destroy]
    K --> L[12. 清理]

1.2 ❌ 错误示范

候选人原话:"Bean 生命周期就是:创建 -> 设置属性 -> 初始化 -> 销毁。"

问题诊断

  • 只说了大概流程,没有具体节点
  • 不知道Aware接口的具体作用时机
  • 不知道 BeanPostProcessor 在初始化前后的两次机会
  • 不知道 init-method 和 InitializingBean 的关系

【面试官心理】 Bean 生命周期的每个节点都是一个"钩子",Spring 框架本身(@Autowired、@Resource、AOP)就挂在这些钩子上工作。知道这些节点,才能理解 Spring 是怎么扩展的,才能理解为什么某些操作(循环依赖、AOP)会在特定阶段出问题。

1.3 12个关键节点详解

第1步:实例化(Instantiation)

// AbstractAutowireCapableBeanFactory.createBeanInstance()
BeanWrapper instanceWrapper = createBeanInstance(
    beanName, mbd, constructorOrFactoryMethod, args);

// 使用策略模式选择实例化方式
InstantiationStrategy strategy = new CglibSubclassingInstantiationStrategy();
Object bean = strategy.instantiate(mbd, beanName, owner, ctor, args);

// 关键:此时 Bean 实例已经创建出来了
// 但还没有任何属性、还没有设置Aware接口

第2步:实例化后处理(Early Bean Reference)

// AbstractAutowireCapableBeanFactory.doCreateBean()
protected void doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    BeanWrapper instanceWrapper = createBeanInstance(...);

    // 将工厂加入三级缓存,用于解决循环依赖
    // 注意:构造器注入在此之前就卡死了,所以无法提前暴露
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    // 此时填充属性还未执行
    populateBean(beanName, mbd, instanceWrapper);
}

第3步:属性填充(PopulateBean)

// AbstractAutowireCapableBeanFactory.populateBean()
populateBean(beanName, mbd, instanceWrapper);

// 处理 @Autowired、@Resource 等注解
// AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues()
// 对所有属性进行依赖注入

第4步:Aware 接口回调

// AbstractAutowireCapableBeanFactory.invokeAwareMethods()
private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
    }
    if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(getClassLoader());
    }
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(this);
    }
}

// ApplicationContextAware 在 ApplicationContextAwareProcessor 中处理
// ApplicationContextAwareProcessor.postProcessBeforeInitialization()

Aware 接口顺序

// Spring 内部的执行顺序(不可改变)
BeanNameAware      → setBeanName()          // Bean 的名字
BeanClassLoaderAware → setBeanClassLoader() // 类加载器
BeanFactoryAware   → setBeanFactory()      // BeanFactory 引用
// ---- ApplicationContext 特有 ----
EnvironmentAware   → setEnvironment()      // 环境变量
EmbeddedValueResolverAware → setEmbeddedValueResolver()  // 值解析器
ApplicationContextAware    → setApplicationContext()      // ApplicationContext 引用
⚠️

BeanFactoryAware 和 ApplicationContextAware 是互斥的。如果你实现了 BeanFactoryAware,Spring 会回调它;如果你实现了 ApplicationContextAware,Spring 会通过 ApplicationContextAwareProcessor 来回调。两者混用时,Spring 会优先使用 ApplicationContextAwareProcessor。

第5步:BeanPostProcessor 前置处理

// AbstractAutowireCapableBeanFactory.initializeBean()
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // Aware 回调已经完成

    // 第5步:BeanPostProcessor.postProcessBeforeInitialization()
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

    // 第6步:初始化方法

    // 第7步:BeanPostProcessor.postProcessAfterInitialization()
    wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}

Spring 内置的 BeanPostProcessor(按执行顺序)

顺序后置处理器作用
1ApplicationContextAwareProcessor回调 Aware 接口(ApplicationContext 系列)
2BeanValidationPostProcessorBean 校验(JSR-303)
3InitDestroyAnnotationBeanPostProcessor处理 @PostConstruct 和 @PreDestroy
4AutowiredAnnotationBeanPostProcessor处理 @Autowired、@Value
5RequiredAnnotationBeanPostProcessor处理 @Required
6CommonAnnotationBeanPostProcessor处理 @Resource、@WebServiceRef 等
7AsyncAnnotationBeanPostProcessor处理 @Async
8...其他自定义后置处理器

第6步:初始化方法

// 执行顺序:先 InitializingBean,再 init-method

// 方式一:InitializingBean 接口
public class UserService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑
    }
}

// 方式二:@PostConstruct 注解(推荐,优先级高于 InitializingBean)
public class UserService {
    @PostConstruct
    public void init() {
        // 初始化逻辑
    }
}

// 方式三:init-method 配置
// <bean id="userService" class="com.example.UserService" init-method="init"/>

// 方式四:@Bean(initMethod = "init")
// @Bean(initMethod = "init")
// public UserService userService() { return new UserService(); }

执行顺序@PostConstruct -> InitializingBean.afterPropertiesSet() -> init-method

// InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()
public Object postProcessBeforeInitialization(
        Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    metadata.invokeInitMethods(bean, beanName);  // 执行 @PostConstruct
    return bean;
}

// AbstractAutowireCapableBeanFactory.invokeInitMethods()
protected void invokeInitMethods(String beanName, Object bean,
        RootBeanDefinition mbd) throws Throwable {

    // 先执行 @PostConstruct(通过 BeanPostProcessor)

    // 再执行 InitializingBean
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet();
    }

    // 最后执行 init-method
    String initMethodName = mbd.getInitMethodName();
    if (StringUtils.hasLength(initMethodName)) {
        invokeCustomInitMethod(beanName, bean, mbd);
    }
}

第7步:BeanPostProcessor 后置处理

// 在初始化方法执行后,BeanPostProcessor 再次介入
// 这里是 AOP 代理创建的关键时机!
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

// 如果有切面切中了这个 Bean
// AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()
// 会在这里创建代理对象,替换原始 Bean
💡

BeanPostProcessor 的后置处理是 AOP 代理创建的核心时机。Spring AOP 通过 AbstractAutoProxyCreator.postProcessAfterInitialization() 在这里判断是否需要为 Bean 创建代理。如果一个 Bean 被 AOP 切中,这里返回的就不是原始对象,而是代理对象了。

二、Bean 销毁生命周期 🟡

2.1 销毁顺序

// 销毁的执行顺序
// 1. @PreDestroy 注解的方法
// 2. DisposableBean.destroy()
// 3. destroy-method 配置的方法
public class UserService {

    @PreDestroy
    public void beforeDestroy() {
        // 第一步执行
    }
}

public class UserServiceImpl implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        // 第二步执行
    }
}

// destroy-method
// <bean id="userService" destroy-method="cleanup"/>

三、Bean 作用域 🟡

3.1 四种作用域

作用域说明使用场景
singleton单例(默认),容器中只有一个实例无状态 Bean
prototype原型,每次 getBean 都创建新实例有状态 Bean
request每个 HTTP 请求创建一个实例Web 请求上下文
session每个 HTTP Session 创建一个实例用户会话
@Scope("prototype")
@Component
public class OrderService {
    // 每次 getBean 都创建新实例
}

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class UserContext {
    // 代理模式解决注入问题
    // 在 request 作用域 Bean 注入到 singleton Bean 时必须使用代理
}

3.2 prototype 作用域的坑

@Service
public class OrderService {
    @Autowired
    private OrderDAO orderDAO;  // orderDAO 是 prototype 作用域
}

// 问题:OrderService 是 singleton,orderDAO 只注入一次
// 每次调用的都是同一个 orderDAO 实例!

// 解决方式:延迟获取
@Service
public class OrderService {
    @Autowired
    private ObjectFactory<OrderDAO> orderDAOFactory;

    public void createOrder() {
        OrderDAO dao = orderDAOFactory.getObject();  // 每次获取新实例
    }
}

四、自定义 BeanPostProcessor 🟡

4.1 实战案例

// 自定义 BeanPostProcessor:自动记录 Bean 创建日志
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(
            Object bean, String beanName) throws BeansException {
        System.out.println("Bean [" + beanName + "] 开始初始化");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(
            Object bean, String beanName) throws BeansException {
        System.out.println("Bean [" + beanName + "] 初始化完成");
        return bean;
    }
}

4.2 InstantiationAwareBeanPostProcessor

// 更强大的后置处理器:在实例化前后都可以干预
@Component
public class MyInstantiationAwareBeanPostProcessor
        extends InstantiationAwareBeanPostProcessorAdapter {

    // 实例化之前返回替代品
    @Override
    public Object postProcessBeforeInstantiation(
            Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == OrderService.class) {
            // 返回一个代理对象,完全替代默认实例化
            return createProxy(beanClass);
        }
        return null;  // 返回 null 表示继续默认实例化
    }

    // 实例化之后、属性填充之前
    @Override
    public boolean postProcessAfterInstantiation(
            Object bean, String beanName) throws BeansException {
        // 返回 false 可以阻止属性填充
        return true;
    }
}

五、生产避坑 🟡

5.1 初始化顺序导致的 NPE

@Component
public class UserService implements InitializingBean {

    @Autowired
    private OrderService orderService;

    private String config;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 问题:此时 orderService 可能还没注入!
        // afterPropertiesSet 在属性填充后执行,但如果是循环依赖...
        orderService.getOrder();
    }
}

5.2 循环依赖与代理对象的坑

@Component
public class A {
    @Autowired
    private B b;

    public void doSomething() {
        // 假设这里需要 A 的代理对象
        A proxy = (A) AopContext.currentProxy();
    }
}

@Component
public class B {
    @Autowired
    private A a;
}

如果 A 有 AOP 切面,在构造器注入循环依赖下,AopContext.currentProxy() 会返回原始对象(因为 Bean 还没完成初始化,代理还没创建)。

5.3 destroy-method 的遗漏

@Service
public class MyService {
    private ThreadPoolExecutor executor;

    @PostConstruct
    public void init() {
        executor = new ThreadPoolExecutor(...);
    }

    // 问题:没有 destroy-method,线程池不会被关闭!
    // 如果 Bean 实现了 DisposableBean 或配置了 destroy-method,就能正常关闭
    @PreDestroy
    public void shutdown() {
        executor.shutdown();
    }
}

六、工程选型 🟢

6.1 初始化方式的选择

方式推荐场景优先级
@PostConstruct通用初始化,推荐使用最高
InitializingBean需要实现接口的场景
init-methodXML 配置遗留项目
@Bean(initMethod)Java Config 风格

【面试官心理】 我通常会问候选人:"如果 @PostConstruct 和 InitializingBean 同时存在,谁先执行?" 能答出"@PostConstruct 先执行"并解释原因的,是真正看过源码的。如果连这个都答错,说明要么没看过源码,要么记混了。

七、面试追问链 🔴

第一层:基本流程 面试官问:"Spring Bean 的生命周期有哪些阶段?" 候选人答:"创建、属性填充、初始化、销毁..."(说不完整) 考察点:基本认知

第二层:详细节点 面试官追问:"Aware 接口有哪些?它们的执行顺序是什么?" 候选人答:...(容易混淆) 考察点:源码细节

第三层:执行顺序 面试官追问:"@PostConstruct、InitializingBean、init-method 的执行顺序是什么?BeanPostProcessor 在哪里插入?" 候选人答:...(核心难点) 考察点:执行顺序理解

第四层:扩展应用 面试官追问:"如果要自定义 Bean 的创建过程,在实例化之前做点什么,该怎么做?" 候选人答:...(InstantiationAwareBeanPostProcessor) 考察点:实际应用

第五层:生产问题 面试官追问:"Bean A 依赖 B,B 依赖 C,C 依赖 A,构造器注入和 setter 注入的循环依赖处理有什么不同?" 候选人答:...(综合应用) 考察点:深度理解