实例化是“造出空壳”,初始化是“填满并激活”:实例化仅调用构造方法创建原始对象,字段全为默认值且无依赖注入;初始化则包含属性填充、aware回调、beanpostprocessor前置处理、@postconstruct/afterpropertiesset/init-method执行及后置处理,最终使bean可安全使用。

实例化是“造出空壳”,初始化是“填满并激活”——这是最核心的本质区别。
实例化:只负责创建对象,不设值、不调逻辑
这一步仅在 JVM 堆中分配内存,调用构造方法(可能是无参、有参或工厂方法),生成一个原始对象。此时所有字段都是默认值(int=0,String=null,List=null),没有任何依赖注入,也不执行任何业务逻辑。
- 对应源码中的
createBeanInstance()方法 - 发生在
getBean()首次触发、且缓存中无该 Bean 时 - 即使 Bean 定义了
@PostConstruct或实现了InitializingBean,此阶段也完全不涉及
初始化:从赋值开始,到可安全使用为止
初始化不是单一动作,而是一整段流程,起点是属性填充,终点是对象真正就绪。它确保 Bean 不仅存在,而且已装配好依赖、完成自检、准备好服务。
- 第一步是 属性赋值(populateBean):把
@Autowired的依赖、@Value的配置、XML 中的<property></property>全部注入进去 - 第二步是 生命周期回调执行:依次调用
BeanPostProcessor.postProcessBeforeInitialization→@PostConstruct/afterPropertiesSet()/init-method→BeanPostProcessor.postProcessAfterInitialization - 只有全部走完,这个 Bean 才算“初始化完成”,才能被其他 Bean 安全注入或调用
为什么必须分两步?关键在解耦和可控性
如果把赋值和校验混在构造里,会导致:
- 构造函数过重,难以单元测试(比如依赖未 mock 就要 new 出来)
- 循环依赖无法解决(A 构造时就要 B,B 构造时又要 A,死锁)——Spring 正是靠“先实例化、后填充”的两阶段,配合三级缓存破局
- 无法统一拦截处理(如 AOP 代理必须在初始化后才可织入,否则代理对象还没配好依赖)
典型时间线(以 singleton Bean 为例)
构造方法执行 → 对象诞生(实例化完成)→ 注入字段/Setter → 调用 @PostConstruct → 执行 init-method → 返回可用 Bean(初始化完成)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











