【面试突击八】Spring IOC:Bean 创建流程全解析(从 getBean 到 AOP 代理生成)

Spring IOC:Bean 创建流程全解析(从 getBean 到 AOP 代理生成)

想把 Spring IOC 讲清楚,核心就一句话:Spring 在你调用 getBean() 或容器启动预实例化单例时,会按固定生命周期把"BeanDefinition 图纸"加工成"可用 Bean(可能是代理)",并放进单例池。

本文围绕 Bean 创建主链路 讲透:创建入口、生命周期步骤、关键扩展点(BFPP/BPP)、AOP 代理生成点、循环依赖的插入位置,帮助你达到"能讲"的水平。


0. 先明确两个对象:BeanDefinition vs Bean

  • BeanDefinition:Bean 的"图纸"(类名、scope、依赖、初始化方法、是否懒加载等)。容器启动时已解析并注册好。
  • Bean 实例:运行期的对象(你最终注入/拿到的对象,可能是原始对象,也可能是代理对象)。

1. Bean 是什么时候被创建的?

两种典型时机:

  1. 容器启动阶段 :创建所有 非 lazy 的单例
    发生在 finishBeanFactoryInitialization() 里(本质是循环调用 getBean())。
  2. 运行时按需创建 :你第一次 getBean(),或注入触发创建(依赖链)
    典型:某个 Bean 是 @Lazy、或 scope=prototype。

2. 主入口:doGetBean(拿 Bean 的总入口)

当你调用 getBean("xxx"),最终会走到:

  • AbstractBeanFactory#doGetBean

它会做几件大事(逻辑上):

  1. 先查缓存(单例池里是否已有成品)
  2. 没有就创建 (走 createBean / doCreateBean
  3. 处理依赖(dependsOn、循环依赖、FactoryBean 等)
  4. 返回最终对象(可能是代理,也可能是 FactoryBean 产物)

3. Bean 创建核心:doCreateBean("生产线")

Spring 创建一个普通 Bean(非 FactoryBean)的核心流程在:

  • AbstractAutowireCapableBeanFactory#doCreateBean

可以用四段来记:
实例化 Instantiation → 属性填充 Populate → 初始化 Initialization → 注册销毁 Destroy

下面逐段展开。


4. 第一步:实例化(Instantiation)------"造出毛坯房"

4.1 发生了什么?

Spring 会根据 BeanDefinition 决定怎么 new 对象:

  • 构造器注入 / 无参构造
  • 工厂方法(@Bean、Factory Method)
  • Supplier(函数式创建)

最终得到一个 原始对象 rawBean:属性大多还是默认值 / null。

4.2 关键点:实例化后,可能"提前暴露引用"

如果是 单例 + 允许循环依赖,Spring 会在属性填充前做一件重要的事:

  • 把一个 ObjectFactory 放入三级缓存 singletonFactories
  • 允许别的 Bean 在循环依赖场景下拿到"早期引用"(可能触发提前代理)

注意:这一步不等于把 Bean 放进一级缓存,它只是提供"可能用得上的早期引用"。


5. 第二步:属性填充(PopulateBean)------"装修+装家具"

5.1 发生了什么?

Spring 会把依赖注入进来:

  • @Autowired / @Resource / @Value
  • XML property
  • 自动装配 byType/byName

注入过程中如果发现依赖 Bean 没有创建,会递归触发依赖 Bean 的创建(依赖链就是在这里展开的)。

5.2 循环依赖通常在这里被触发

典型 setter 循环依赖:

  • A 填充属性需要 B → 创建 B
  • B 填充属性需要 A → 此时 A 还没完成
    Spring 会尝试从三级缓存 ObjectFactory拿到 A 的早期引用 → 注入给 B → 打破循环

6. 第三步:初始化(Initialization)------"通电自检 + 功能增强"

初始化阶段发生在 initializeBean() 中,是面试最容易问深的部分。它的顺序非常固定:

6.1 Aware 回调(让 Bean "认识容器")

如果 Bean 实现了这些接口,Spring 会注入相关上下文:

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware
  • ApplicationContextAware(通过 ApplicationContextAwareProcessor

6.2 BeanPostProcessor Before

执行所有 BeanPostProcessor#postProcessBeforeInitialization(bean, beanName)

  • 常见:处理 @PostConstruct 的前置准备等

6.3 执行初始化方法(init callbacks)

按顺序可能包含:

  1. @PostConstruct
  2. InitializingBean#afterPropertiesSet
  3. init-method(XML 或 @Bean(initMethod=...)

6.4 BeanPostProcessor After(极其重要:AOP 代理通常在这里生成)

执行所有 BeanPostProcessor#postProcessAfterInitialization(bean, beanName)

AOP(以及事务)通常就是在这个阶段把原始对象替换成代理对象的。

  • 负责 AOP 的典型处理器:AnnotationAwareAspectJAutoProxyCreator
  • 它会判断当前 Bean 是否命中切点(Pointcut)
    • 命中:创建 JDK/CGLIB 代理并返回 proxy
    • 不命中:原样返回 bean
  • Spring 使用"后置处理器的返回值"作为最终 Bean
    => 实现"偷梁换柱":rawBean 进去,proxyBean 出来

所以你注入的 Service 往往是 $Proxy...xxx$$EnhancerBySpringCGLIB$$...


7. 第四步:注册销毁回调(Destroy)------"贴上回收说明"

如果 Bean 是单例,且存在销毁逻辑,Spring 会注册:

  • DisposableBean#destroy
  • destroy-method
  • @PreDestroy

容器关闭(context.close())时统一调用。


8. 最终:进入单例池(一级缓存)

当 Bean 完全创建完成后(包含可能的代理替换):

  • 放入 singletonObjects(一级缓存)
  • 同时清理早期缓存(如果曾发生循环依赖/提前暴露)

后续再次获取同名单例 Bean,直接从一级缓存返回。


9. 一张 ASCII 总览图(背诵友好)

text 复制代码
getBean()
  |
  v
doGetBean()
  |
  +-- 命中 singletonObjects ? ----> return (成品/代理)
  |
  v
createBean()
  |
  v
doCreateBean()
  |
  +-- 1) 实例化 createBeanInstance()  ---> rawBean
  |         |
  |         +-- (单例&允许循环依赖) addSingletonFactory() 放三级缓存
  |
  +-- 2) 属性填充 populateBean() -----> @Autowired 注入依赖
  |         |
  |         +-- 可能触发循环依赖:从三级缓存拿早期引用
  |
  +-- 3) 初始化 initializeBean()
  |         |
  |         +-- Aware 回调
  |         +-- BPP.before
  |         +-- init-method/@PostConstruct/afterPropertiesSet
  |         +-- BPP.after  ---> 【AOP/事务代理通常在这里生成】
  |
  +-- 4) 注册销毁回调 registerDisposableBeanIfNecessary()
  |
  v
addSingleton() 放入 singletonObjects(一级缓存)
return 最终对象(可能是 proxy)

10. 面试"能讲"的总结话术

Spring Bean 的创建核心分为四步:实例化、属性填充、初始化、销毁。

实例化阶段通过反射或工厂方法创建原始对象;属性填充阶段完成依赖注入并可能触发循环依赖处理;初始化阶段会执行 Aware、初始化方法以及 BeanPostProcessor。
AOP/事务代理通常在 postProcessAfterInitialization 里生成并替换原始对象 ,最后成品 Bean 放进一级缓存 singletonObjects,后续直接复用。


相关推荐
熏鱼的小迷弟Liu40 分钟前
【消息队列】RabbitMQ的基本架构?
面试·架构·rabbitmq
NAGNIP7 小时前
一文搞懂机器学习中的特征降维!
算法·面试
NAGNIP7 小时前
一文搞懂机器学习中的特征构造!
算法·面试
毕设源码-郭学长7 小时前
【开题答辩全过程】以 基于SpringBoot技术的美妆销售系统为例,包含答辩的问题和答案
java·spring boot·后端
梨落秋霜7 小时前
Python入门篇【文件处理】
android·java·python
N***H4867 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
Java 码农7 小时前
RabbitMQ集群部署方案及配置指南03
java·python·rabbitmq
哈库纳玛塔塔7 小时前
放弃 MyBatis,拥抱新一代 Java 数据访问库
java·开发语言·数据库·mybatis·orm·dbvisitor
S***q3778 小时前
Spring Boot管理用户数据
java·spring boot·后端
天“码”行空9 小时前
java面向对象的三大特性之一多态
java·开发语言·jvm