哈喽大家好,我是小米!
今天这篇文章,咱们来聊聊一个Spring面试中“永远的经典题”:
使用 @Autowired 注解自动装配的过程是怎样的?
说实话,这个问题虽然看起来简单,但真要说清楚,背后的技术细节那是相当多!
别怕,我今天就用一个“讲故事”的方式,带你从表层用法一直追到底层源码逻辑,把这个知识点讲得明明白白、通通透透,面试官听了都得说一句“懂得真细”~
故事从一个@Autowired开始……
我们先来设个场景:
我刚跳槽到一家互联网公司的架构组,公司用的是Spring Boot,全套微服务架构,Bean 管理基本都是用的注解。
我负责一个叫 订单中台 的模块,代码大致是这样的:
你看,很简单,就是通过 @Autowired 把 PaymentService 注入进来。
但是你有没有想过:
1、Spring 是怎么知道 paymentService 是谁?
2、它是在什么时候把这个对象“塞”进来的?
3、如果有多个 PaymentService 实现,它又是怎么判断该用哪个的?
这些问题一层层抛出来,就像是剥洋葱,越剥越精彩!
先聊聊:@Autowired 到底是个啥?
首先你得知道,@Autowired 是 Spring 提供的一个 依赖注入注解,它的核心功能就是:
“帮你把需要的 Bean 自动装配进来。”
它可以用在:
- 构造方法上
- 字段上
- Setter方法上
Spring 通过它来做 依赖注入(Dependency Injection,简称DI) 。
小知识点:
Spring 支持三种依赖注入方式:构造器注入、Setter注入和字段注入,@Autowired 三种都支持。
自动装配,Spring 是怎么“找对象”的?
自动装配的原理,其实可以分三步理解:
第一步:找所有需要装配的地方
Spring 在 初始化 Bean 的时候,会分析这个类中是否有被 @Autowired 注解标记的字段、方法、构造函数。如果有,它就会把这个地方标记为“需要注入”。
这部分的操作是在 Bean 后处理器(BeanPostProcessor) 里完成的。
核心类:AutowiredAnnotationBeanPostProcessor
它是 Spring 容器中非常重要的一个 Bean 后置处理器,它的作用是:
在 Spring 初始化 Bean 时,解析所有标注了 @Autowired 和 @Value 的字段或方法,并完成注入。
这一步其实发生在 Instantiation -> Populate -> Initialization 的过程中,属于 Bean 的生命周期中的“populate”阶段。
第二步:找到合适的Bean
Spring 会根据字段的类型来找出所有可以注入的 Bean 候选者。
假如你有这样两个 Bean:
然后你在 OrderService 中这么写:
这时 Spring 就会很“头疼”——
“哎哟我去,这两个 Bean 都能注入,到底选谁?”
这时候,如果你没加任何说明(比如没加 @Qualifier),就会抛出 NoUniqueBeanDefinitionException 异常。
所以,Spring 的选择逻辑其实是这样的:
- 先根据类型找候选Bean;
- 如果有多个候选,就看是否有使用 @Qualifier 限定;
- 如果没有,就看有没有标注了 @Primary 的 Bean;
- 如果还是有歧义,就报错。
你可以通过 @Primary 或 @Qualifier("beanName") 指定注入哪个 Bean。
第三步:执行注入
当找到了合适的 Bean,Spring 就会用 反射 的方式把这个 Bean 设置进对应的字段或者调用 Setter 方法,或者作为构造函数参数注入。
核心代码是通过 Java 的反射 API 实现,比如 Field.setAccessible(true) + Field.set()。
注意!Spring 中依赖注入是通过反射完成的,而不是传统的 new。
这一过程的实现就在 AutowiredAnnotationBeanPostProcessor 中。
源码时间:真实过程是怎样的?
程序员不能只讲道理,咱得看源码!
我来带你看几个关键方法:
类:AutowiredAnnotationBeanPostProcessor
这是核心处理器,继承了 InstantiationAwareBeanPostProcessorAdapter,所以它可以参与 Bean 的创建过程。
方法一:postProcessProperties
它做了两件事:
- 找到需要注入的字段和方法;
- 执行注入操作。
方法二:findAutowiringMetadata
这个方法会扫描整个类,找出哪些字段、方法上有 @Autowired。
内部会构建一个 InjectionMetadata 对象,里面存着所有“要注入的点”。
方法三:inject
这个方法是真正干活的!
它会遍历所有注入点,通过 BeanFactory.resolveDependency() 找到 Bean,然后反射注入进去。
最终注入依赖的核心就是:
就是这么朴实无华地把对象塞进去了
构造函数注入有什么不同?
当你用的是构造器注入:
这时候处理器是另外一个阶段介入的——Spring 会在创建对象时,就用构造器来传入依赖。
构造器注入的处理主要由:
这个方法负责解析最合适的构造方法,并注入依赖。
它的选择规则是:
- 如果构造器上有 @Autowired,就优先使用;
- 如果只有一个构造器,那就默认用它;
- 否则就会尝试匹配参数最多的构造函数。
那为什么推荐构造器注入?
小米的经验总结:
字段注入易写易读,但不利于测试;构造器注入代码更健壮,便于单测和依赖控制。
构造器注入的优点包括:
- 明确依赖关系
- 不依赖 Spring 的反射注入,纯 Java 构造可测试
- 能确保对象创建时依赖齐全
如果你以后写一些核心服务类,建议用构造器注入!
面试官还有后招吗?
当然有!下面是几个延伸问题,面试中很可能被问到:
问题一:@Autowired 和 @Resource 有什么区别?
问题二:@Autowired 什么时候起作用?
在 Bean 实例化之后,初始化之前(也就是 populate 阶段)。
可以结合 Bean 生命周期流程复习一下:
- 实例化
- 设置属性(populate)
- 初始化(调用 init-method)
- AOP代理增强等
总结一下
今天我们从一个最简单的注解 @Autowired 出发,一层层剖析了 Spring 的自动装配机制,过程中经历了:
- Bean 后置处理器的介入
- 依赖查找与解析
- 反射注入的实现原理
- 构造器与字段注入的异同
你会发现,看似一个简单的注解,背后竟然藏着这么多技术细节!
面试答题模板
如果你在面试中遇到这个问题,可以这样回答:
“@Autowired 是 Spring 提供的自动装配注解,它通过 AutowiredAnnotationBeanPostProcessor 实现依赖注入。这个处理器在 Bean 初始化阶段介入,扫描字段或方法的注解,找到需要注入的地方,然后根据类型从容器中查找 Bean,通过反射完成注入。如果存在多个实现,可以通过 @Qualifier 或 @Primary 来指定注入对象。构造器注入则在实例化时完成,推荐用于强依赖场景。”
是不是比你之前的回答稳多了
END
如果你觉得今天的内容有收获,欢迎点赞+关注,后续我还会讲更多源码故事和Spring家族的面试题!
我们下期再见,拜拜啦~
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!