【面试突击八】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,后续直接复用。


相关推荐
NE_STOP36 分钟前
MyBatis-mybatis入门与增删改查
java
Lee川2 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i4 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
孟陬4 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
想用offer打牌4 小时前
一站式了解四种限流算法
java·后端·go
绝无仅有4 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有4 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
华仔啊4 小时前
Java 开发千万别给布尔变量加 is 前缀!很容易背锅
java
AAA梅狸猫5 小时前
Looper.loop() 循环机制
面试