type
status
date
slug
summary
tags
category
icon
password
catalog
sort
写给:
  • 刚学 Spring 想一口气吃透 Bean 的新手
  • 被循环依赖、作用域、生命周期虐到秃头的中级玩家
  • 想在面试里把“Bean 怎么来的”讲到源码级的卷王

🧭 目录速览

  1. Spring Bean 是啥?和普通 Java 对象有啥区别?
  1. 容器是怎么“认”出它的?
  1. .class 到“可用对象”的完整旅程
  1. 生命周期:11 个阶段一次讲透
  1. 作用域:单例、原型、request、session 的底层实现
  1. 高级特性:条件、延迟、动态注册
  1. 常见坑 & 解决方案
  1. 最佳实践:怎么选作用域?
  1. 未来趋势:Spring 6.x & WebFlux
  1. 一张图总结

1️⃣ Spring Bean 是啥?和普通 Java 对象有啥区别? {#1}

📚 官方文档坐标:
Spring Framework Reference → Core → IoC Container → 1.3. Bean Overview “A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.” Bean 就是一个被 Spring 全程托管的对象:帮你 new,帮你注入,帮你销毁,一条龙!
对比维度
普通 Java 对象
Spring Bean
创建方式
new User()
容器反射创建
生命周期
手动 new / GC
容器全程托管
依赖注入
手动 setXxx()
@Autowired 自动注入
销毁
等待 GC
@PreDestroy 回调
一句话:Bean 就是被 Spring “领养”的 Java 对象,吃喝拉撒全管!

2️⃣ 容器是怎么“认”出它的? {#2}

📚 官方文档坐标:
步骤
官方类
关键方法
源码链接
① 定位资源
PathMatchingResourcePatternResolver#getResources
getResources("classpath*:com/example/**/*.class")
② 候选过滤
ClassPathScanningCandidateComponentProvider#findCandidateComponents
过滤 @Component
③ 解析注解
AnnotatedBeanDefinitionReader#registerBean
@Component@Lazy 等元数据
④ 注册
DefaultListableBeanFactory#registerBeanDefinition
beanDefinitionMap.put(beanName, definition)

2.1 容器三层结构(面试常问)

  • BeanFactory:只负责“拿 bean”
  • ApplicationContext:加料版 BeanFactory,能发事件、读 i18n、扫注解

2.2 容器识别 Bean 的 3 步曲

步骤
关键类
说明
① 扫描
ClassPathBeanDefinitionScanner
@Component@Service
② 解析
AnnotatedBeanDefinitionReader
把注解转成 BeanDefinition
③ 注册
DefaultListableBeanFactory
存到 Map<String, BeanDefinition>
小抄:容器启动时,会把所有 *.class 变成 BeanDefinition,再按需实例化。

3️⃣ 从 .class 到“可用对象”的完整旅程 {#3}

3.1 单例 Bean 的三级缓存(解决循环依赖)

缓存级别
作用
是否线程安全
一级 singletonObjects
已初始化完成的单例
二级 earlySingletonObjects
早期暴露(未初始化)
三级 singletonFactories
创建早期引用的工厂
面试题:Spring 如何解决 A→B→A 的循环依赖?
答:三级缓存提前暴露 A 的早期引用,B 注入后继续完成 A 的初始化。

4️⃣ 生命周期:11 个阶段一次讲透 {#4}

📚 官方文档坐标:
Core → Container → 1.6. Bean Lifecycle

4.1 生命周期全景

阶段
官方方法
源码行号
① 实例化
AbstractAutowireCapableBeanFactory#createBeanInstance
② 属性注入
populateBean
③ 初始化前
CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization
④ 初始化
InitializingBean#afterPropertiesSet
⑤ 销毁
DisposableBeanAdapter#destroy

4.2 3 种初始化方式 & 执行顺序

方式
示例
顺序
注解
@PostConstruct
1️⃣
接口
InitializingBean#afterPropertiesSet
2️⃣
XML
init-method="customInit"
3️⃣

4.3 销毁方法同理:先 @PreDestroyDisposableBeandestroy-method


5️⃣ 作用域:单例、原型、request、session 的底层实现 {#5}

📚 官方文档坐标:
Core → Container → 1.5. Bean Scopes

5.1 作用域速查表

作用域
实例数
生命周期
线程安全
典型场景
singleton
1
容器 → 关闭
❌ 需手动保证
Service、DAO
prototype
每次 getBean
❌ 无共享
有状态命令对象
request
1/请求
请求 → 响应
✅ 线程隔离
用户请求上下文
session
1/会话
登录 → 登出
✅ 会话隔离
购物车、登录态

5.2 Web 作用域的底层实现

  • request 作用域:存到 HttpServletRequest#setAttribute
  • session 作用域:存到 HttpSession#setAttribute
  • 线程安全:靠 ThreadLocal<RequestAttributes> 隔离

5.3 自定义作用域实战:批处理任务作用域


6️⃣ 高级特性:条件、延迟、动态注册 {#6}

6.1 条件注解:@ConditionalOnClass

  • 官方实现OnClassCondition 判断 ClassUtils.isPresent
  • 场景:只在类路径存在 RedisTemplate 时才注册 RedisCacheManager
  • 原理:在 BeanDefinition 注册阶段就排除不符合条件的 Bean

6.2 延迟加载:@Lazy

  • 单例:默认预实例化,@Lazy 改为首次使用时实例化
  • 原型:无实际意义(本身就是每次创建)

6.3 动态注册:BeanDefinitionRegistryPostProcessor


7️⃣ 常见坑 & 解决方案 {#7}

坑点
现象
根因
解决
单例持有 request Bean
内存泄漏
单例长期引用 request 作用域 Bean
使用代理模式 @Scope(proxyMode = TARGET_CLASS)
原型 Bean 循环依赖
BeanCurrentlyInCreationException
每次创建都触发新实例,无限递归
设计重构,引入中间层
session Bean 未序列化
集群部署报错
未实现 Serializable
实现 Serializable,或使用分布式会话

8️⃣ 最佳实践:怎么选作用域? {#8}

8.1 决策树(30 秒版)

8.2 性能与资源权衡

作用域
创建开销
GC 压力
适用场景
singleton
无状态服务
prototype
有状态短暂对象
request
请求上下文

9️⃣ 未来趋势:Spring 6.x & WebFlux {#9}

9.1 GraalVM 原生镜像

  • AOT 编译:构建时生成 BeanDefinition,减少运行时反射
  • 限制:Bean 构造函数需 public,避免动态 Class.forName

9.2 响应式 Bean 生命周期

  • 非阻塞初始化@PostConstruct 可返回 Mono<Void>
  • Context 替代 request 作用域:通过 Reactor Context 传递请求信息

🔟 一张图总结 {#10}


写在最后:Spring Bean 就像乐高积木,选对作用域、用好生命周期,才能搭出既稳又快的系统。收藏本文,面试不慌,调优不慌! 🛠️

参考

  1. Spring Boot启动流程
  1. Bean加载链路
  1. Bean 深度剖析:生命周期、加载机制与作用域
MySQL行值表达式:从“一脸懵”到“玩明白”的实战指南; Mysql RVC,Mysql元组比较,Row Value Constructor/ComparisonJDK 8至24浮点数转换底层原理与优化演进
Loading...