-
死磕Spring之IoC篇 - @Autowired 等注解的实现原理
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
开始阅读这一系列文章之前,建议先查看《深入了解 Spring IoC(面试题)》这一篇文章
该系列其他文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》
@Autowired 等注解的实现原理
在上一篇《Bean 的属性填充阶段》文章中讲到,在创建一个 Bean 的实例对象后,会对这个 Bean 进行属性填充。在属性填充的过程中,获取到已定义的属性值,然后会通过 InstantiationAwareBeanPostProcessor 对该属性值进行处理,最后通过反射机制将属性值设置到这个 Bean 中。在 Spring 内部有以下两个 InstantiationAwareBeanPostProcessor 处理器:
- AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 注解标注的属性,获取对应属性值
- CommonAnnotationBeanPostProcessor,会解析 @Resource 注解标注的属性,获取对应的属性值
本文将会分析这两个处理器的实现,以及涉及到的相关对象
这两个处理器在哪被注册?
在前面的《解析自定义标签(XML 文件)》 和 《BeanDefinition 的解析过程(面向注解)》文章中可以知道,在 XML 文件中的 <context:component-scan />
标签的处理过程中,会底层借助于 ClassPathBeanDefinitionScanner
扫描器,去扫描指定路径下符合条件(@Component 注解)的 BeanDefinition 们,关于 @ComponentScan 注解的解析也是借助于这个扫描器实现的。扫描过程如下:
在第 <3>
步会调用 AnnotationConfigUtils 的 registerAnnotationConfigProcessors(BeanDefinitionRegistry)
方法,如下:
在这个方法中可以看到会注册 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 两个处理器,然后在 Spring 应用上下文刷新阶段会将其初始化并添加至 AbstractBeanFactory 的 beanPostProcessors
集合中,那么接下来我们先来分析这两个处理器
回顾 Bean 的创建过程
第一步:回到《Bean 的创建过程》文章中的“对 RootBeanDefinition 加工处理”小节,会调用这个方法:
调用所有 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition 方法对 RootBeanDefinition 进行加工处理,例如:
-
AutowiredAnnotationBeanPostProcessor,会先解析出
@Autowired
和@Value
注解标注的属性的注入元信息,后续进行依赖注入 -
CommonAnnotationBeanPostProcessor,会先解析出
@Resource
注解标注的属性的注入元信息,后续进行依赖注入,它也会找到@PostConstruct
和@PreDestroy
注解标注的方法,并构建一个 LifecycleMetadata 对象,用于后续生命周期中的初始化和销毁
第二步:回到《Bean 的创建过程》文章中的“属性填充”小节,该过程会进行下面的处理:
这里不会调用所有 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法对 pvs
(MutablePropertyValues)属性值对象进行处理,例如:
-
AutowiredAnnotationBeanPostProcessor,会根据前面解析出来的
@Autowired
和@Value
注解标注的属性的注入元信息,进行依赖注入 -
CommonAnnotationBeanPostProcessor,会根据前面解析出来的
@Resource
注解标注的属性的注入元信息,进行依赖注入
可以看到@Autowired
、@Value
和 @Resource
注解的实现就是基于这两个处理器实现的,接下来我们来看看这两个处理器的具体实现
AutowiredAnnotationBeanPostProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
,主要处理 @Autowired
和 @Value
注解进行依赖注入
体系结构
可以看到 AutowiredAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 和 InstantiationAwareBeanPostProcessor 两个接口
构造方法
可以看到会添加 @Autowired
和 @Value
两个注解,如果存在 JSR-330 的 javax.inject.Inject
注解,也是支持的
postProcessMergedBeanDefinition 方法
postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
方法,找到 @Autowired
和 @Value
注解标注的字段(或方法)的元信息,如下:
直接调用 findAutowiringMetadata(...)
方法获取这个 Bean 的注入元信息对象
1. findAutowiringMetadata 方法
首先尝试从缓存中获取这个 Bean 对应的注入元信息对象,没有找到的话则调用 buildAutowiringMetadata(final Class<?> clazz)
构建一个,然后再放入缓存中
2. buildAutowiringMetadata 方法
过程如下:
-
创建
currElements
集合,用于保存@Autowired
、@Value
注解标注的字段 -
遍历这个 Class 对象的所有字段
-
找到该字段的
@Autowired
或者@Value
注解,返回ann
对象,没有的话返回空对象,则直接跳过不进行下面的操作 - 进行过滤,static 修饰的字段不进行注入
-
获取注解中的
required
配置 -
根据该字段和
required
构建一个 AutowiredFieldElement 对象,添加至currElements
-
找到该字段的
-
遍历这个 Class 对象的所有方法
- 尝试找到这个方法的桥接方法,没有的话就是本身这个方法
- 如果是桥接方法则直接跳过
-
找到该方法的
@Autowired
或者@Value
注解,返回ann
对象,没有的话返回空对象,则直接跳过不进行下面的操作 - 进行过滤,static 修饰的方法不进行注入
-
获取注解中的
required
配置 -
构建一个 AutowiredMethodElement 对象,添加至
currElements
- 找到父类,循环遍历
- 根据从这个 Bean 解析出来的所有 InjectedElement 对象生成一个 InjectionMetadata 注入元信息对象,并返回
整个过程很简单,就是解析出所有 @Autowired
或者 @Value
注解标注的方法或者字段,然后构建一个 InjectionMetadata 注入元信息对象
postProcessProperties 方法
postProcessProperties(PropertyValues pvs, Object bean, String beanName)
方法,根据 @Autowired
和 @Value
注解标注的字段(或方法)的元信息进行依赖注入,如下:
先找到这个 Bean 的注入元信息对象,上面已经讲过了,然后调用其 inject(...)
方法,这里先来看到 InjectionMetadata
这个对象
InjectionMetadata 注入元信息对象
org.springframework.beans.factory.annotation.InjectionMetadata
,某个 Bean 的注入元信息对象
可以看到注入方法非常简单,就是遍历所有的 InjectedElement 对象,调用他们的 inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)
方法
AutowiredFieldElement
AutowiredAnnotationBeanPostProcessor 的私有内部类,注入字段对象,如下:
直接看到 inject(...)
方法,注入的过程如下:
-
获取
field
字段 - 如果进行缓存了,则尝试从缓存中获取
-
否则,开始进行解析
-
创建一个依赖注入描述器
desc
-
【核心】通过
DefaultListableBeanFactory#resolveDependency(...)
方法,找到这个字段对应的 Bean(们) - 和缓存相关,如果有必要则将本次找到的注入对象缓存起来,避免下次再进行解析
-
创建一个依赖注入描述器
- 如果获取到该字段对应的对象,则进行属性赋值(依赖注入),底层就是通过反射机制为该字段赋值
可以看到整个的核心在于通过 DefaultListableBeanFactory#resolveDependency(...)
方法找到字段对应的 Bean,这里也许是一个集合对象,所以也可能找到的是多个 Bean,该方法在后面进行分析
AutowiredMethodElement
AutowiredAnnotationBeanPostProcessor 的私有内部类,注入方法对象,如下:
直接看到 inject(...)
方法,注入的过程如下:
-
获取
method
方法 - 如果进行缓存了,则尝试从缓存中获取方法参数对象
-
否则,开始进行解析
-
获取方法的参数类型集合
paramTypes
,根据参数位置确定参数 -
构建一个依赖注入描述器数组
descriptors
,用于保存后续创建的对象 -
根据参数顺序遍历该方法的参数
-
为第
i
个方法参数创建一个 MethodParameter 对象 -
创建依赖描述器
currDesc
,并添加至descriptors
数组 -
【核心】通过
DefaultListableBeanFactory#resolveDependency(...)
方法,找到这个方法参数对应的 Bean(们) - 和缓存相关,如果有必要则将本次找到的方法参数对象缓存起来,避免下次再进行解析
-
为第
-
获取方法的参数类型集合
- 如果找到该方法的参数(们),则进行属性赋值(依赖注入),底层就是通过反射机制调用该方法
可以看到整个的核心也是通过 DefaultListableBeanFactory#resolveDependency(...)
方法找到方法参数对应的 Bean,该方法在后面进行分析
CommonAnnotationBeanPostProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
,主要处理 @Resource
注解进行依赖注入,以及 @PostConstruct
和 @PreDestroy
生命周期注解的处理
体系结构
可以看到 CommonAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 和 InstantiationAwareBeanPostProcessor 两个接口,还实现了 DestructionAwareBeanPostProcessor 接口,用于生命周期中的初始化和销毁的处理
构造方法
可以看到会设置初始化注解为 @PostConstruct
,销毁注解为 @PreDestroy
,这两个注解都是 JSR-250 注解;另外如果存在 javax.xml.ws.WebServiceRef
和 javax.ejb.EJB
注解也是会进行设置的
postProcessMergedBeanDefinition 方法
postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
方法,找到 @PostConstruct
和 @PreDestroy
注解标注的方法,并构建 LifecycleMetadata 对象,找到 @Resource
注解标注的字段(或方法)的元信息,如下:
整个的过程原理和 AutowiredAnnotationBeanPostProcessor 差不多,先从缓存中获取,未命中则调用对应的方法进行构建,下面先来看看父类中的方法
buildLifecycleMetadata 方法
整个过程比较简单,找到这个 Bean 中 @PostConstruct
和 @PreDestroy
注解标注的方法,然后构建一个 LifecycleMetadata 生命周期元信息对象
buildResourceMetadata 方法
整个过程也比较简单,解析出这个 Bean 带有 @Resource
注解的所有字段(或方法),构建成对应的 ResourceElement 对象,然后再构建成一个 InjectionMetadata 注入元信息对象
postProcessProperties 方法
postProcessProperties(PropertyValues pvs, Object bean, String beanName)
方法,根据 @Resource
注解标注的字段(或方法)的元信息进行依赖注入,如下:
先找到这个 Bean 的注入元信息对象,上面已经讲过了,然后调用其 inject(...)
方法,该对象上面已经讲过了,实际就是调用其内部 InjectedElement 的 inject(...)
方法
postProcessBeforeInitialization 方法
初始化 Bean 的时候会先执行 @PostConstruct
标注的初始化方法
postProcessBeforeDestruction 方法
销毁 Bean 的时候先执行 @PreDestroy
注解标注的销毁方法
ResourceElement
CommonAnnotationBeanPostProcessor 的私有内部类,@Resource 注入字段(或方法)对象
构造方法
ResourceElement 的构造方法会通过 @Resource
注解和该字段(或方法)解析出基本信息
可以看到还继承了 InjectionMetadata 的静态内部类 InjectedElement,我们先来看到这个类的 inject(...)
方法
inject 方法
不管是字段还是方法,底层都是通过反射机制进行赋值或者调用,都会调用 getResourceToInject(...)
方法获取到字段值或者方法参数
getResourceToInject 方法
如果是延迟加载,则调用 buildLazyResourceProxy(...)
方法返回一个代理对象,如下:
否则,调用 getResource(...)
方法获取注入对象
getResource 方法
前面的判断忽略掉,直接看到最后会调用 autowireResource(...)
方法,并返回注入信息
autowireResource 方法
@Resource
注解相比于 @Autowired
注解的处理更加复杂点,可以如果 @Resource
指定了名称,则直接通过依赖查找获取该名称的 Bean,否则,和 @Autowired
一样去调用 DefaultListableBeanFactory#resolveDependency(...)
方法,找到对应的注入对象,该方法在后面进行分析
1. resolveDependency 处理依赖方法
resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, TypeConverter typeConverter)
方法,找到对应的依赖 Bean,该方法在《Bean 的创建过程》中也提到了,获取 Bean 的实例对象时,构造器注入的参数也是通过该方法获取的,本文的依赖注入底层也是通过该方法实现的,这里我们对该方法一探究竟
过程如下:
- 设置参数名称探测器,例如通过它获取方法参数的名称
-
如果依赖类型为 Optional 类型,则调用
createOptionalDependency(...)
方法,先将descriptor
注入表述器封装成 NestedDependencyDescriptor 对象,底层处理和下面的5.2
相同 -
否则,如果依赖类型为 ObjectFactory 或 ObjectProvider 类型,直接返回一个
DependencyObjectProvider
私有内部类对象,并没有获取到实例的 Bean,需要调用其 getObject() 方法获取目标对象 -
否则,如果依赖类型为 javax.inject.Provider 类型,直接返回一个
Jsr330Provider
私有内部类对象,该对象也继承 DependencyObjectProvider -
否则,通用的处理逻辑
- 先通过 AutowireCandidateResolver 尝试获取一个代理对象,延迟依赖注入则会返回一个代理对象
-
如果上面没有返回代理对象,则进行处理,调用
doResolveDependency(...)
方法
我们需要关注的是上面的第 5.2
步所调用 doResolveDependency(...)
方法,这一步是底层实现
2. doResolveDependency 底层处理依赖方法
依赖处理的过程稍微有点复杂,如下:
-
针对给定的工厂给定一个快捷实现的方式,暂时忽略
例如考虑一些预先解析的信息,在进入所有 Bean 的常规类型匹配算法之前,解析算法将首先尝试通过此方法解析快捷方式
-
获取注解中的 value 对应的值,例如
@Value
、@Qualifier
注解配置的 value 属性值,注意 @Autowired 没有 value 属性配置- 解析注解中的 value,因为可能是占位符,需要获取到相应的数据
- 进行类型转换,并返回
-
解析复合的依赖对象(Array、Collection、Map 类型),获取该属性元素类型的 Bean 们,调用
resolveMultipleBeans(...)
方法底层和下面第
4
步原理一样,这里会将descriptor
封装成 MultiElementDescriptor 类型,如果找到了则直接返回 -
查找与类型相匹配的 Bean 们,调用
findAutowireCandidates(...)
方法返回结果:key -> beanName;value -> 对应的 Bean
-
如果一个都没找到
-
如果
@Autowired
配置的 required 为 true,表示必须,则抛出异常 - 否则,返回一个空对象
-
如果
-
如果匹配的 Bean 有多个,则需要找出最优先的那个
-
找到最匹配的那个 Bean,通过
@Primary
或者@Priority
来决定,或者通过名称决定,调用determineAutowireCandidate(...)
方法 - 如果没有找到最匹配的 Bean,则抛出 NoUniqueBeanDefinitionException 异常
-
获取到最匹配的 Bean,传值引用给
instanceCandidate
-
找到最匹配的那个 Bean,通过
-
否则,只有一个 Bean,则直接使用其作为最匹配的 Bean
-
将依赖注入的 Bean 的名称添加至方法入参
autowiredBeanNames
集合,里面保存依赖注入的 beanName -
如果匹配的 Bean 是 Class 对象,则根据其 beanName 依赖查找到对应的 Bean
-
返回依赖注入的 Bean
关于上面第 3
步对于符合依赖对象的处理这里不做详细分析,因为底层和第 4
步一样,接下来分析上面第 4
、6
步所调用的方法
findAutowireCandidates 方法
findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor)
方法,找到符合条件的依赖注入的 Bean 们,如下:
过程大致如下:
-
从当前上下文找到该类型的 Bean 们(根据类型)
-
定义一个 Map 对象
result
,用于保存符合条件的 Bean -
遍历 Spring 内部已处理的依赖对象集合,例如你依赖注入 BeanFactory 类型的对象,则拿到的是 DefaultListableBeanFactory 对象,依赖注入 ResourceLoader、ApplicationEventPublisher、ApplicationContext 类型的对象, 拿到的就是当前 Spring 上下文 ApplicationContext 对象
-
遍历第
1
步找到的 Bean 的名称们-
如果满足下面两个条件,则添加至
result
集合中如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)、符合注入的条件
-
-
如果没有找到符合条件的 Bean,则再尝试获取
-
再次遍历第
1
步找到的 Bean 的名称们 -
如果满足下面三个条件,则添加至
result
集合中如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)、符合注入的条件、不是复合类型,或者有
@Qualifier
注解
-
-
如果还没有找到符合条件的 Bean,则再尝试获取,和上面第
5
步的区别在于必须是自引用(这个 Bean 是在需要依赖它的 Bean 的内部定义的) -
返回
result
,符合条件的 Bean
总结下来:从当前上下文找到所有该类型的依赖注入对象然后返回,注意,如果你依赖注入的对象就是本身这个 Bean 内部定义的对象有特殊处理。
例如注入一个集合对象,元素类型的 Bean 有一个是定义在本身这个 Bean 的内部,如果仅有这个 Bean 则会注入进行;如果除了本身这个 Bean 内部定义了,其他地方也定义了,那么本身这个 Bean 内部定义的 Bean 是不会被注入的;因为是自引用的 Bean 不会优先考虑,除非一个都没找到,才会尝试获取自引用的 Bean
determineAutowireCandidate 方法
determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor)
方法,找到最匹配的那个依赖注入对象,如下:
如果找到了多个匹配的依赖注入对象,则需要找到最匹配的那个 Bean,过程大致如下:
-
尝试获取一个
@Primary
注解标注的 Bean,如果有找到多个则会抛出异常 -
如果第
1
步找到了则直接返回 -
尝试找到
@Priority
注解优先级最高的那个 Bean,如果存在相同的优先级则会抛出异常 -
如果第
3
步找到了则直接返回 -
兜底方法,遍历所有的 Bean
- 如果满足下面其中一个条件则直接返回:该 Bean 为 Spring 内部可处理的 Bean(例如 ApplicationContext、BeanFactory)、名称相匹配
- 上面都没选出来则返回一个空对象
总结
@Autowired
和 @Resource
两个注解的区别:
- 前者是 Spring 注解,后者是 JSR 注解
- 两个注解都可以通过类型注入 Bean,而后者还可以通过指定名称,通过名称注入对应的 Bean
-
前者可以通过设置
required
为false
以支持找不到依赖对象的时候不进行注入,而后者必须找到依赖对象进行注入,找不到则会抛出异常
本文讲述了 @Autowired
、@Value
和 @Resource
等注解的实现原理,在《Bean 的创建过程》中我们可以了解到,在 Spring Bean 生命周期的很多阶段都可以通过相应的 BeanPostProcessor 处理器进行扩展,其中《Bean 的属性填充阶段》会通过 InstantiationAwareBeanPostProcessor
对 Bean 进行处理,有以下两个处理器:
-
AutowiredAnnotationBeanPostProcessor,主要处理
@Autowired
和@Value
注解进行依赖注入 -
CommonAnnotationBeanPostProcessor,主要处理
@Resource
注解进行依赖注入,以及@PostConstruct
和@PreDestroy
生命周期注解的处理
原理就是找到注解标注的字段(或方法),创建对应的注入元信息对象,然后根据该元信息对象进行注入(反射机制),底层都会通过 DefaultListableBeanFactory#resolveDependency
方法实现的,找到符合条件的 Bean(根据类型),然后筛选出最匹配的那个依赖注入对象。
疑问:@Bean 等注解的实现原理又是怎样的呢?别急,在后续文章进行分析
__EOF__