当前位置: 首页 > news >正文

Spring 自动注入是怎么实现的?从 @Component 到 @Autowired 的完整流程

你写过这样的代码:

@Component
public class UserService { }@Service
public class OrderService {@Autowiredprivate UserService userService;
}

没手动 new,也没赋值,但 userService 就能直接用。
这背后不是魔法,而是一套精心分层、各司其职的自动化机制

下面,我们用最清晰的逻辑,回答几个核心问题:


1. 容器启动时,谁做了什么?

ClassPathBeanDefinitionScanner(组件扫描器)
做了什么:在应用启动时,扫描你标记了 @Component@Service 的类,把它们转换成 BeanDefinition(Bean 的“蓝图”),注册进容器。
怎么做:通过反射读取类上的注解,提取类名、作用域、是否懒加载等信息,存入 DefaultListableBeanFactory 内部的 beanDefinitionMap
为什么:Spring 必须先知道“有哪些 Bean 要管理”,才能决定何时创建、如何注入。
不这么做会怎样:容器根本不知道 UserService 是个 Bean,后续一切注入无从谈起。

✅ 此时 还没创建任何对象,只有“菜谱”。


2. Bean 是什么时候创建的?谁创建的?

DefaultListableBeanFactory(Bean 工厂)
做了什么:在容器刷新(refresh())的最后阶段,调用 preInstantiateSingletons()提前创建所有非 @Lazy 的单例 Bean
怎么做

  • 遍历所有 BeanDefinition
  • 对每个 singleton + 非懒加载 的 Bean,调用 getBean("userService")
  • 最终通过 AbstractAutowireCapableBeanFactory.doCreateBean() 执行 new UserService()
  • 创建完成后,把实例存入 singletonObjects(一级缓存)。

为什么:提前暴露问题(如依赖缺失、循环依赖),避免运行时突然崩溃。
不这么做会怎样:所有 Bean 延迟到第一次使用才创建,启动快但运行时可能报错,调试困难。

@Lazy 的 Bean 会跳过这一步,等到第一次被依赖或 getBean() 时才创建。


3. @Autowired 是怎么“塞”进去的?

AutowiredAnnotationBeanPostProcessor(自动注入处理器)
做了什么:在 OrderService 对象创建后、初始化前,扫描它的字段,发现 @Autowired private UserService userService,然后从容器中拿到 userService 实例,通过反射赋值塞进去。
怎么做

  • 调用 beanFactory.getBean("userService") 获取已创建的实例(来自 singletonObjects);
  • 调用 field.set(orderService, userService) 完成注入。

为什么:实现“声明式依赖”,开发者无需手动管理对象关系。
不这么做会怎样userService 字段永远是 null,调用时直接 NullPointerException

✅ 这个过程发生在 populateBean() 阶段,是 doCreateBean() 的一部分。


4. 如果 A 依赖 B,B 又依赖 A,怎么办?

DefaultSingletonBeanRegistry(单例注册表)
做了什么:通过三级缓存机制,在 A 还没完全创建好时,就允许 B 引用 A 的“早期引用”。
三级缓存是

  • 一级 singletonObjects:成品 Bean(最终放入容器的对象);
  • 二级 earlySingletonObjects:已解析的早期引用;
  • 三级 singletonFactoriesObjectFactory,按需生成早期引用(支持 AOP 代理)。

怎么做

  • 创建 A 时,先 new A(),然后立即注册一个 ObjectFactory 到三级缓存;
  • 当 B 需要 A 时,从三级缓存拿到工厂,调用 getObject() 得到 A(可能是代理);
  • 这个早期引用会被缓存到二级,避免重复生成;
  • A 完全初始化后,放入一级缓存,清除二、三级。

为什么:既要解决循环依赖,又要保证 AOP 代理一致性(B 拿到的必须是代理对象)。

不这么做会怎样

  • 没有缓存 → 循环依赖直接 StackOverflow;
  • 只有两级缓存(直接存原始对象)→ B 拿到原始 A,容器最终存代理 A → 同一个 Bean 有两个实例,事务失效

总结

  • 容器启动Scanner 扫描 → 注册 BeanDefinition(菜谱);
  • Bean 创建BeanFactory 预实例化 → doCreateBean → 存入 singletonObjects
  • 依赖注入AutowiredAnnotationBeanPostProcessor 反射赋值;
  • 循环依赖DefaultSingletonBeanRegistry 用三级缓存安全暴露早期引用。

这一切,都是确定的、可追踪的、有源码可证的工程逻辑,不是玄学。

下次再看到 @Autowired,你就知道:

是谁,在哪一步,用什么方法,把哪个对象,放进了你的字段里。

http://www.dtcms.com/a/512512.html

相关文章:

  • 基于springboot的基于智能推荐的卫生健康系统开发与设计
  • 技术面:Spring(循环依赖,spring与springboot的区别)
  • 网站建设相关法律python破解wordpress
  • 高并发系统网络优化:TCP 参数调优、HTTP 协议优化(HTTP_2、HTTPS)
  • PostgreSQL跨数据库授权查询
  • 构建自定义命令行工具 - 打造专属指令体
  • 今日反弹有玄机:外围利好是助力!
  • 门户网站定制青岛网站建设公司招聘
  • 腾讯云做网站怎么样长沙装修公司口碑比较好的
  • 做翻译 网站php网站建设找哪家好
  • 网站建设中栏目是什么南京做代账会计在哪个网站上找
  • 2025年HR 数字化转型:从工具应用到组织能力重构的深度变革
  • 做网站需要看的书公司网站建设工作内容
  • 4.7.Cypher 中的子查询
  • 公共服务平台网站建设方案下载wordpress建站程序
  • 数据结构八大排序:希尔排序-原理解析+C语言实现+优化+面试题
  • 数据结构八大排序:归并排序-原理+C语言实现+优化+面试题
  • 无站点推广就是不在网上推广广州网站建设 美词
  • 佛山网站建设邓先生阳江市网络问政平台登录
  • 图像增广——弹性形变
  • 视频推拉流平台EasyDSS技术特点解析及多元应用场景剖析
  • 做网站需要学php吗北京公司注册代理
  • 职高门户网站建设标准wordpress火车头发布模板
  • CycleGAN实现MNIST与SVHN风格迁移
  • AVL树手撕,超详细图文详解
  • ZeroTier虚拟局域网内搭建DNS服务器
  • 网络与通信安全课程复习汇总3——身份认证
  • 诸城网站做的好的创网站 灵感
  • C++多线程、STL
  • 自己做的网站怎么加入微信支付哪个网站做五金冲压的