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,原因:
- 不侵入 Bean 类(可以标注在任意方法上)
- JSR-250 标准,Spring 和 Jakarta EE 都支持
- 比 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:
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 { }
// 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'
解决方式:
- 使用
@Lazy 延迟加载
- 使用 Setter 注入代替构造器注入
- 重构代码,消除循环依赖
3.2 初始化失败导致服务不可用
如果某个 Bean 的初始化方法抛异常,整个 ApplicationContext 启动失败,服务不可用。
@Service
public class ConfigLoader {
@PostConstruct
public void load() {
// 从外部加载配置,如果失败则启动失败
throw new RuntimeException("配置加载失败!");
}
}
解决方式:
- 让初始化方法有兜底逻辑
- 使用
@ConditionalOnProperty 控制 Bean 是否加载
- 使用
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 生命周期回调方式对比
4.2 不同场景下的最佳实践
- 配置加载:使用
@PostConstruct,失败时让容器启动失败
- 资源注册(ZK、Redis):使用
@PostConstruct + @PreDestroy,确保注册和注销成对出现
- 连接池初始化:使用
@PostConstruct 或 InitializingBean,确保连接在使用前建立
- 健康检查:使用
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 的经验。