Bean 生命周期

候选人小王在面试阿里 P6 时,面试官直接问道:

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

小王回答:"实例化、属性填充、初始化、销毁。"

面试官追问:"那初始化之前和之后分别会做什么?"

小王说:"会调用...一些回调?"

面试官:"哪些回调?BeanNameAware、BeanFactoryAware、ApplicationContextAware 你用过哪个?"

小王彻底语塞。

【面试官心理】 这道题我用来测试候选人是否真正理解 Spring 容器的扩展性体系。知道"四步走"的人占 80%,能说出初始化前后的回调接口的占 40%,能在生产环境用过这些接口的只有 10%。这道题能答到最后的,基本都是 P6+ 并且有实际项目经验的。


一、核心问题 🔴

1.1 问题拆解

第一层:基本流程

  • "Bean 的生命周期有哪些阶段?"
  • "实例化和初始化有什么区别?"

第二层:回调接口

  • "初始化前后的回调接口有哪些?"
  • "Aware 接口是干什么的?你用过哪些?"

第三层:源码实现

  • "Spring 是在哪个方法里调这些回调的?"
  • "BeanPostProcessor 和 InitializingBean 有什么区别?"

第四层:生产实践

  • "你在项目里用过哪些生命周期回调?"
  • "怎么让 Bean 在初始化时检查配置是否正确?"

1.2 ❌ 错误示范

候选人原话 A:"Bean 生命周期就是创建、使用、销毁三步。"

问题诊断

  • 太笼统,完全没有技术细节
  • 说明只用过 @Bean@Service,没深入研究过
  • 面试官追问任何一个细节都会崩

候选人原话 B:"有 InitializingBean 和 DisposableBean 接口,可以实现 afterPropertiesSet 和 destroy 方法。"

问题诊断

  • 只知道两个接口,不知道还有更多
  • 不知道这些接口的调用顺序
  • 不知道还有 @PostConstruct、BeanPostProcessor 等

候选人原话 C:"Aware 接口是用来感知容器信息的,我用过 ApplicationContextAware。"

问题诊断

  • 知道有 Aware 接口,但不知道 Spring 内置了多少种
  • 没理解 Aware 接口的设计意图
  • 说不清 Aware 接口在生命周期中的位置

1.3 标准回答

P5 回答:四阶段概述

Spring Bean 的完整生命周期分为四个大阶段:

┌─────────────────────────────────────────────────────────────┐
│                     1. 实例化(Instantiation)               │
│   调用构造函数,创建 Bean 实例                              │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                     2. 属性填充(Population)               │
│   注入依赖、设置属性值                                      │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                     3. 初始化(Initialization)             │
│   Aware 接口回调 → BeanPostProcessor 前置 → init-method    │
│   → BeanPostProcessor 后置                                  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                     4. 销毁(Destruction)                   │
│   DisposableBean.destroy() → destroy-method               │
└─────────────────────────────────────────────────────────────┘

1.4 追问升级

追问 1:完整生命周期的 12 个步骤

这是 P6 必须掌握的细节:

Bean 生命周期完整流程:

1. 实例化
   └─ 调用构造函数,创建 Java 对象(new)
   └─ 位置:AbstractAutowireCapableBeanFactory.createBeanInstance()

2. 属性填充(PopulateBean)
   └─ 注入普通属性(基本类型、String、集合等)
   └─ 注入依赖 Bean(autowire by name / by type)
   └─ 位置:AbstractAutowireCapableBeanFactory.populateBean()

3. Aware 接口回调
   └─ BeanNameAware → setBeanName()
   └─ BeanClassLoaderAware → setBeanClassLoader()
   └─ BeanFactoryAware → setBeanFactory()
   └─ EnvironmentAware → setEnvironment()
   └─ EmbeddedValueResolverAware → setEmbeddedValueResolver()
   └─ ResourceLoaderAware → setResourceLoader()(仅 ApplicationContext)
   └─ ApplicationEventPublisherAware → setApplicationEventPublisher()
   └─ MessageSourceAware → setMessageSource()
   └─ ApplicationContextAware → setApplicationContext()

4. BeanPostProcessor.postProcessBeforeInitialization()
   └─ 位置:AbstractAutowireCapableBeanFactory.initializeBean()

5. @PostConstruct 标注的方法
   └─ 由 InitDestroyAnnotationBeanPostProcessor 调用

6. InitializingBean.afterPropertiesSet()

7. 自定义 init-method(@Bean(initMethod = "..."))

8. BeanPostProcessor.postProcessAfterInitialization()
   └─ AOP 代理在此创建

9. Bean 正常使用

10. @PreDestroy 标注的方法

11. DisposableBean.destroy()

12. 自定义 destroy-method(@Bean(destroyMethod = "..."))

核心源码在 AbstractAutowireCapableBeanFactory#initializeBean()

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // 1. Aware 接口回调
    invokeAwareMethods(beanName, bean);

    // 2. BeanPostProcessor 前置处理
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

    // 3. 调用 init-method
    invokeInitMethods(beanName, wrappedBean, mbd);

    // 4. BeanPostProcessor 后置处理
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    return wrappedBean;
}

private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext((ApplicationContext) getBeanFactory());
        }
    }
}

protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) {
    boolean isInitializingBean = bean instanceof InitializingBean;
    if (isInitializingBean) {
        // 2. InitializingBean.afterPropertiesSet()
        ((InitializingBean) bean).afterPropertiesSet();
    }
    // 3. 自定义 init-method
    if (mbd.hasInitMethodName()) {
        invokeCustomInitMethod(beanName, bean, mbd);
    }
}

【面试官心理】 我问他完整的生命周期,其实不是在考记忆力。我是想看他有没有用过这些扩展点,比如:Aware 接口有没有用过(通常用于获取容器内部的资源)、BeanPostProcessor 有没有自定义过(通常用于 AOP 增强)。知道流程只能说明看过文章,能用过才是真的做过项目。

追问 2:@PostConstruct 和 @PreDestroy 是怎么被调用的?

这是源码级别的追问:

// InitDestroyAnnotationBeanPostProcessor 源码
public class InitDestroyAnnotationBeanPostProcessor
        implements DestructionAwareBeanPostProcessor, Ordered {

    // 管理 @PostConstruct 和 @PreDestroy 方法
    private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
        List<LifecycleElement> initMethods = new ArrayList<>();
        List<LifecycleElement> destroyMethods = new ArrayList<>();

        ReflectionUtils.doWithLocalMethods(clazz, method -> {
            if (method.hasAnnotation(PostConstruct.class)) {
                initMethods.add(new LifecycleElement(method));
            }
            if (method.hasAnnotation(PreDestroy.class)) {
                destroyMethods.add(new LifecycleElement(method));
            }
        });

        return new LifecycleMetadata(initMethods, destroyMethods);
    }

    // postProcessBeforeInitialization 时调用 @PostConstruct
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        metadata.invokeInitMethods(bean, beanName);
        return bean;
    }

    // postProcessBeforeDestruction 时调用 @PreDestroy
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) {
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        metadata.invokeDestroyMethods(bean, beanName);
    }
}

@PostConstruct vs InitializingBean vs init-method 的执行顺序

postProcessBeforeInitialization
  ├─ 1. @PostConstruct(如果标注了)
  ├─ 2. InitializingBean.afterPropertiesSet()
  └─ 3. 自定义 init-method

推荐使用 @PostConstruct,原因:

  1. 不侵入 Bean 类(可以标注在任意方法上)
  2. JSR-250 标准,Spring 和 Jakarta EE 都支持
  3. 比 InitializingBean 更清晰,不强制实现接口
💡

Spring Boot 2.x 推荐使用 @PostConstruct + @PreDestroy,但在 Spring Cloud 服务中,如果 Bean 涉及到 Spring Cloud Config 的刷新,可能需要用 SmartLifecycle 来替代 @PreDestroy,因为 Config 刷新时会关闭旧的 ApplicationContext。

追问 3:Aware 接口用过哪些?什么时候用?

这是生产实践的追问:

@Service
public class MyService implements BeanNameAware, ApplicationContextAware {

    private String beanName;
    private ApplicationContext applicationContext;

    // 获取当前 Bean 在容器中的名字
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("当前 Bean 的名字是:" + name);
    }

    // 获取 ApplicationContext,从而获取容器中任意 Bean
    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        this.applicationContext = ctx;
    }

    // 可以在任何时候获取其他 Bean
    public void doSomething() {
        OtherService other = applicationContext.getBean(OtherService.class);
    }
}

实际生产中的典型应用:

  • @Component 的 Bean 需要在启动时注册到外部系统(Redis、ZK)
  • 需要在 Bean 中获取容器本身的配置
  • 需要实现动态 Bean 获取
@Service
public class RegistryService implements InitializingBean, DisposableBean {

    private String registrationId;

    @Override
    public void afterPropertiesSet() {
        // 向 ZK 注册当前服务
        this.registrationId = zkClient.createEphemeral("/service/" + getHost(), null);
        System.out.println("服务注册成功,ID = " + registrationId);
    }

    @Override
    public void destroy() {
        // 从 ZK 注销
        zkClient.delete(registrationId);
        System.out.println("服务注销成功");
    }
}

追问 4:BeanPostProcessor 是什么?AOP 代理是怎么创建的?

这是 P7 的关键追问:

// BeanPostProcessor 是一个扩展点,Spring 内部用它做了很多事
public interface BeanPostProcessor {
    // 初始化之前调用
    Object postProcessBeforeInitialization(Object bean, String beanName);

    // 初始化之后调用
    Object postProcessAfterInitialization(Object bean, String beanName);
}

Spring 内部注册了哪些 BeanPostProcessor:

BeanPostProcessor作用执行时机
ApplicationContextAwareProcessor调用 Aware 接口Before
ConfigurationClassPostProcessor$BeanMethodInterceptor处理 @Bean 方法Before
AutowiredAnnotationBeanPostProcessor处理 @Autowired/@ValueBefore
CommonAnnotationBeanPostProcessor处理 @PostConstruct/@PreDestroy/@ResourceBefore
AnnotationAwareAspectJAutoProxyCreator创建 AOP 代理After
ApplicationListenerDetector检测 ApplicationListenerAfter

AOP 代理的创建位置就在 postProcessAfterInitialization()

// AbstractAutoProxyCreator.java
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null) {
        // 如果需要代理,则在这里创建代理对象
        // wrapIfNecessary 会判断是否匹配切点
        return wrapIfNecessary(bean, beanName, getCacheKey(bean.getClass(), beanName));
    }
    return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1. 判断是否已经处理过
    // 2. 判断是否应该创建代理(是否有匹配的切点)
    // 3. 创建代理(JDK 动态代理 或 CGLIB)
    // 4. 返回代理对象
}
⚠️

这里有个大坑:BeanPostProcessor 是在 Bean 初始化阶段被调用的,不是 Bean 创建阶段。所以如果你在 @PostConstruct 里调用被 @Transactional 标注的方法,这个调用不会经过代理对象,事务不会生效!

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder() {
        // 通过代理对象调用,事务生效
        orderRepository.save(new Order());
    }

    @PostConstruct
    public void init() {
        // 这里直接调用 this.createOrder()
        // this 是原始对象,不是代理对象
        // 事务不生效!
        createOrder(); // ❌ 错误:没有走代理
        ((OrderService) AopContext.currentProxy()).createOrder(); // ✅ 正确
    }
}

二、延伸问题 🟡

2.1 作用域对生命周期的影响

不同作用域的 Bean,生命周期行为不同:

@Component
@Scope("prototype") // 原型作用域
public class PrototypeBean { }

@Component
@Scope("singleton") // 单例作用域(默认)
public class SingletonBean { }
作用域创建时机销毁时机线程安全
singleton容器启动时容器关闭时需要自己保证
prototype每次 getBean 时GC 回收(Spring 不管理)天然隔离
request每次 HTTP 请求时HTTP 请求结束时线程隔离
session每次 HTTP Session 创建时Session 失效时Session 隔离
applicationServletContext 启动时ServletContext 销毁时应用级别共享
// request 作用域示例
@Controller
@RequestScope
public class RequestContextHolder {
    private Map<String, Object> attributes = new HashMap<>();
    // 每个请求都有独立的 attributes
}

// prototype 作用域:每次 getBean 都创建新实例
@Component
@Scope("prototype")
public class HeavyObject {
    // 适合重量级对象,构造开销大
}

2.2 init-method 和 destroy-method 的配置方式

// 方式1:@Bean 指定
@Configuration
public class AppConfig {
    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

// 方式2:@PostConstruct + @PreDestroy
@Service
public class MyService {
    @PostConstruct
    public void init() { }

    @PreDestroy
    public void cleanup() { }
}

// 方式3:实现接口
@Service
public class MyService implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() { }

    @Override
    public void destroy() { }
}

// 方式4:全局默认(Spring Boot 2.x)
// application.yml
spring.main.lazy-initialization=false
// 全局指定所有 Bean 的 init/destroy 方法前缀
spring.bean.lifecycle.default-init-method-type=common

三、生产避坑

3.1 Bean 初始化顺序问题

Spring 中 Bean 的创建顺序按照依赖拓扑排序确定。如果 A 依赖 B,B 依赖 C,则创建顺序为 C → B → A。

但循环依赖会导致启动失败:

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a; // 循环依赖!
}

错误信息:BeanCurrentlyInCreationException: Error creating bean with name 'a'

解决方式:

  1. 使用 @Lazy 延迟加载
  2. 使用 Setter 注入代替构造器注入
  3. 重构代码,消除循环依赖

3.2 初始化失败导致服务不可用

如果某个 Bean 的初始化方法抛异常,整个 ApplicationContext 启动失败,服务不可用。

@Service
public class ConfigLoader {
    @PostConstruct
    public void load() {
        // 从外部加载配置,如果失败则启动失败
        throw new RuntimeException("配置加载失败!");
    }
}

解决方式:

  1. 让初始化方法有兜底逻辑
  2. 使用 @ConditionalOnProperty 控制 Bean 是否加载
  3. 使用 SmartInitializingSingleton 延迟到所有 Bean 初始化完成后执行

3.3 单例 Bean 中的线程安全问题

单例 Bean 在多线程环境下共享,如果 Bean 中有可变状态,会出现线程安全问题:

@Service
public class OrderService {
    private int counter = 0; // ❌ 线程不安全

    public void createOrder() {
        counter++; // 竞态条件
    }

    // ✅ 正确做法:不要在单例 Bean 中使用可变状态
    // 或者使用 ThreadLocal
    private ThreadLocal<Integer> counter = new ThreadLocal<>();
}

四、工程选型

4.1 生命周期回调方式对比

方式推荐度原因
@PostConstruct + @PreDestroy⭐⭐⭐⭐⭐JSR-250 标准,不侵入,最推荐
InitializingBean + DisposableBean⭐⭐⭐Spring 专有接口,不推荐
init-method + destroy-method⭐⭐⭐⭐XML/注解配置,推荐在 @Bean 中使用
BeanPostProcessor⭐⭐⭐⭐框架级扩展,业务代码一般不用

4.2 不同场景下的最佳实践

  • 配置加载:使用 @PostConstruct,失败时让容器启动失败
  • 资源注册(ZK、Redis):使用 @PostConstruct + @PreDestroy,确保注册和注销成对出现
  • 连接池初始化:使用 @PostConstructInitializingBean,确保连接在使用前建立
  • 健康检查:使用 SmartLifecycle,实现 isHealthy() 方法
  • 配置刷新:使用 @RefreshScope,在 Config 刷新时重建 Bean
// Spring Cloud 配置刷新示例
@Component
@RefreshScope
public class ConfigProperties {
    @Value("${config.value}")
    private String value;

    // 每次 refresh 都会重新创建实例
    // 可以感知最新的配置值
}

五、面试总结

Bean 生命周期这道题,表面考的是"流程",实际上考的是"扩展性"。

P5 候选人能说出"实例化、属性填充、初始化、销毁"四步。 P6 候选人能说出 Aware 接口回调、BeanPostProcessor 前置/后置、@PostConstruct 等细节。 P7 候选人能说出 AOP 代理的创建时机(postProcessAfterInitialization)、@PostConstruct 中调用事务不生效的原因,以及各种作用域的生命周期差异。

能在这道题上答到最后的,基本都有过框架开发或者深度使用 Spring 的经验。