一、一句话速记(先建立整体感)
实例化 → 属性填充 → Aware接口 → 初始化前 → 初始化 → 初始化后 → 使用 → 销毁
二、完整生命周期流程图(11步)
text
- 实例化(new对象,在堆里分配空间)
↓ - 属性填充(setter注入、@Autowired等)
↓ - BeanNameAware(setBeanName)
↓ - BeanClassLoaderAware(setBeanClassLoader)
↓ - BeanFactoryAware(setBeanFactory)
↓ - EnvironmentAware(setEnvironment,可选)
↓ - BeanPostProcessor.postProcessBeforeInitialization(初始化前)
↓ - InitializingBean.afterPropertiesSet
↓ - 自定义 init-method(@PostConstruct或xml指定的init方法)
↓ - BeanPostProcessor.postProcessAfterInitialization(初始化后)
↓
【此时Bean已就绪,可以被应用程序使用】
↓ - 容器关闭时销毁:
- @PreDestroy
- DisposableBean.destroy
- 自定义 destroy-method
三、每个阶段的详细说明(面试重点)
阶段1:实例化(Instantiation)
做了什么:通过反射或CGLIB创建对象,在堆中分配内存
特征:此时属性还是null,依赖还没注入
相关扩展点:InstantiationAwareBeanPostProcessor
阶段2:属性填充(Populate)
做了什么:自动注入依赖(byName/byType/注解)
核心处理:解析@Autowired、@Value、@Resource
循环依赖处理:此时会暴露半成品对象到二级/三级缓存
阶段3:Aware接口(感知容器)
顺序固定:BeanNameAware → BeanClassLoaderAware → BeanFactoryAware → 其他Aware
作用:让Bean知道自己的一些元信息(bean名称、工厂等)
阶段4:BeanPostProcessor前置处理
方法:postProcessBeforeInitialization
典型应用:@PostConstruct的解析、ApplicationContextAware(在此时处理)
阶段5:初始化(真正干活)
顺序:
InitializingBean.afterPropertiesSet()
自定义init方法(@Bean(initMethod="xxx") 或xml配置)
注意:@PostConstruct实际上是在前置处理器中执行的,严格来说在afterPropertiesSet之前
阶段6:BeanPostProcessor后置处理
方法:postProcessAfterInitialization
典型应用:AOP动态代理就在这里生成
如果Bean需要被代理(比如加了@Transactional、@Async),此时返回的是代理对象
阶段7:使用
从容器中getBean()获取到的就是完整可用的Bean
阶段8:销毁
容器关闭(close()或registerShutdownHook())
顺序:
@PreDestroy
DisposableBean.destroy()
自定义destroy方法
四、完整示例代码(帮你加深记忆)
java
@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
InitializingBean, DisposableBean {
public LifecycleBean() {
System.out.println("1. 实例化");
}
@Autowired
private SomeService someService;
// 2. 属性填充(自动注入)
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
System.out.println("5. BeanFactoryAware");
}
@PostConstruct
public void postConstruct() {
System.out.println("7. @PostConstruct(在初始化前处理器中执行)");
}
@Override
public void afterPropertiesSet() {
System.out.println("8. InitializingBean.afterPropertiesSet");
}
@Bean(initMethod = "customInit")
public void customInit() {
System.out.println("9. 自定义init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("11. @PreDestroy");
}
@Override
public void destroy() {
System.out.println("12. DisposableBean.destroy");
}
}
五、面试常见追问与应对
Q1:BeanPostProcessor和InitializingBean的关系?
BeanPostProcessor是全局处理器,作用于所有Bean
InitializingBean是单个Bean的初始化回调
执行顺序:postProcessBeforeInitialization → afterPropertiesSet → 自定义init → postProcessAfterInitialization
Q2:AOP代理在生命周期哪一步生成?
答: 在postProcessAfterInitialization中。AbstractAutoProxyCreator(BeanPostProcessor实现)判断是否需要代理,如果需要则返回代理对象替换原Bean。
Q3:@PostConstruct为什么比afterPropertiesSet早?
因为@PostConstruct是在CommonAnnotationBeanPostProcessor(前置处理器)中执行的,时机在afterPropertiesSet之前。
Q4:循环依赖中生命周期有什么特殊?
提前暴露半成品到三级缓存,跳过部分Aware和初始化,等依赖注入完成后再继续。
六、面试终极回答模板(背下来)
Spring Bean的生命周期从实例化开始,通过反射创建对象后,进行属性填充(依赖注入),然后执行Aware接口(BeanNameAware、BeanFactoryAware等),接着进入初始化阶段:先执行BeanPostProcessor的前置处理,再执行InitializingBean的afterPropertiesSet,然后是自定义init方法,最后执行BeanPostProcessor的后置处理(AOP代理在此生成)。此时Bean完成初始化,可供使用。容器关闭时,依次执行@PreDestroy、DisposableBean的destroy和自定义destroy方法。
七、记忆口诀
实例填充感知前,初始化和后置连,用完销毁三回调
实例 = 实例化
填充 = 属性填充
感知 = Aware
前 = 初始化前处理器
初始化 = afterPropertiesSet + init-method
后置 = 初始化后处理器
销毁三回调 = @PreDestroy + DisposableBean + destroy-method
为什么要把初始化过程拆成"前-中-后"三个阶段。
让我用一个饭店做菜的类比来解释,然后再说技术原因。
一、用做菜类比(先建立直觉)
假设你要做一道"清蒸鲈鱼":
| 阶段 | 做菜过程 | Spring Bean | 为什么这么分 |
|---|---|---|---|
| 实例化 | 从冰箱拿出鲈鱼(生的) | new对象,属性为null | 先有原材料 |
| 属性填充 | 加葱姜、淋料酒 | 注入依赖 | 准备配料 |
| 前置处理 | 检查鱼是否新鲜、要不要改刀 | @PostConstruct |
初始化前的准备工作 |
| 初始化 | 上锅蒸(核心烹饪) | afterPropertiesSet + init |
真正干活 |
| 后置处理 | 出锅后淋热油、加香菜 | AOP生成代理 | 加工增强 |
关键理解:
@PostConstruct= 蒸之前的腌制/检查afterPropertiesSet= 蒸的过程- AOP代理 = 蒸完之后加的浇头(不是鱼本身)
二、为什么需要"前置处理"(阶段4)?
核心原因:有些初始化工作必须在afterPropertiesSet之前完成
java
@Component
public class UserService {
@PostConstruct
public void init() {
// 这个检查应该在业务初始化之前执行
System.out.println("1. 检查配置是否完整");
System.out.println("2. 预加载缓存数据");
}
@Bean(initMethod = "start")
public void afterPropertiesSet() {
// 业务真正的初始化
System.out.println("3. 开始处理业务逻辑");
}
}
设计意图:
@PostConstruct做准备性工作(检查资源、预热缓存)afterPropertiesSet做业务初始化(真正干活)- 如果把准备工作和业务逻辑混在一起,代码会混乱
典型场景
| 场景 | 为什么必须在之前 |
|---|---|
@PostConstruct |
需要在依赖注入完成后、业务初始化前做一些校验或预热 |
ApplicationContextAware |
需要在初始化之前让Bean拿到容器引用(因为初始化可能要用到容器) |
三、为什么需要"后置处理"(阶段6)?
核心原因:AOP代理必须在原对象初始化完成后才能创建
java
@Service
@Transactional
public class OrderService {
public void createOrder() {
// 业务逻辑
}
}
Spring需要创建一个代理对象 来管理事务。问题来了:什么时候创建这个代理?
不能太早:
- 如果实例化时就创建代理,原对象的属性还没注入(依赖是null)
- 如果属性填充时创建代理,原对象还没初始化(业务逻辑未就绪)
必须在最后:
- 原对象已经完全准备好了(属性注入完成、初始化方法执行完)
- 此时用动态代理包装原对象,返回代理对象
代码示例理解
java
// Spring内部大致逻辑(简化)
Object bean = 实例化(); // 原始OrderService对象
填充属性(bean); // 注入依赖
初始化前处理(bean); // @PostConstruct
初始化(bean); // afterPropertiesSet
// 关键:此时bean已经是一个完整的OrderService对象
// 检查是否需要AOP
if (需要代理(bean)) {
bean = 创建代理(bean); // 返回Proxy对象
}
初始化后处理(bean); // 其他后置处理器
return bean; // 可能返回代理对象
四、三个阶段的核心区别(面试重点)
| 阶段 | 执行时机 | 典型操作 | 对Bean的影响 |
|---|---|---|---|
| 前置处理 | 初始化方法之前 | @PostConstruct、Aware接口 |
不能改变Bean的"本质" |
| 初始化 | 正中间 | afterPropertiesSet、init-method |
业务初始化,不改变Bean类型 |
| 后置处理 | 初始化之后 | AOP代理 、@Async代理 |
可以替换Bean对象 |
关键差异:
- 前置处理:只能对原对象进行操作(设置属性、调用方法)
- 后置处理:可以替换原对象(返回代理对象)
五、为什么要这样设计?三个设计原则
1. 单一职责原则
- 实例化只管创建对象
- 属性填充只管注入依赖
- 初始化只管业务准备
- 后置处理管增强(AOP)
- 每个阶段职责清晰
2. 开闭原则(对扩展开放)
- 如果你想要在所有Bean初始化之前做点什么 → 实现
BeanPostProcessor(前置) - 如果你想要在所有Bean初始化之后做点什么 → 实现
BeanPostProcessor(后置) - 不需要修改Spring源码
3. 模板方法模式
Spring定义好了生命周期骨架,留出扩展点,让开发者按需插入逻辑。
六、完整时序图(帮助理解)
时间轴 →
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[实例化] → 原对象 = new OrderService() (null, null)
↓
[属性填充] → orderService.userDao = userDao
↓
[前置处理] → @PostConstruct → 检查配置
↓
[初始化] → afterPropertiesSet → 加载数据
↓
[后置处理] → 创建CGLIB代理 → 包装原对象
↓
[返回] → 返回代理对象(不是原对象了!)
七、验证代码(亲自跑一下)
java
@Component
public class DemoBean {
@PostConstruct
public void postConstruct() {
System.out.println("1. @PostConstruct: " + this.getClass().getSimpleName());
}
@Override
public void afterPropertiesSet() {
System.out.println("2. afterPropertiesSet: " + this.getClass().getSimpleName());
}
@Bean(initMethod = "init")
public void init() {
System.out.println("3. init-method: " + this.getClass().getSimpleName());
}
}
// 加上AOP
@Aspect
@Component
public class LogAspect {
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("代理执行中...");
return pjp.proceed();
}
}
输出类似:
1. @PostConstruct: DemoBean
2. afterPropertiesSet: DemoBean$$EnhancerByCGLIB ← 已经是代理了
3. init-method: DemoBean$$EnhancerByCGLIB
注意:代理之后,后两个方法是在代理对象上执行的。
八、面试回答模板
Spring把初始化分为三个阶段:
- 前置处理 :在业务初始化之前执行,典型如
@PostConstruct,用于资源检查、预热等准备工作,因为这类操作必须在核心业务逻辑之前完成。- 初始化 :业务真正的初始化,如
afterPropertiesSet,此时依赖已经注入完毕。- 后置处理 :在初始化之后执行,最重要的是AOP代理生成。因为AOP需要基于一个完整的原对象创建代理,既不能太早(依赖没注入),也不能太晚(需要在返回容器前完成)。代理生成后返回的是增强后的代理对象,不是原对象。
一句话总结 :前置做准备工作,中间做业务初始化,后置做增强代理------这是开闭原则 和单一职责的体现,让每个扩展点职责清晰。