内容总结
依赖注入是 Bean 创建流程中的一个步骤, 入口在 refresh - finishBeanFactoryInitialization(实例化所有非懒加载的单例 Bean) - getBean - doGetBean - createBean - doCreateBean - populateBean, 内部分为两个流程, 原生注入和扩展注入, 原生注入是 XML autowire=byType/byName 的方式, 现在基本已经不用了, 扩展注入是通过 BeanPostProcessor 机制, 实现了 @Autowired, @Value, @Resource 等注解的字段/入参的注入
原生注入, 分为 ByName 和 ByType 两种方式, ByName 方式是通过解析类里面的全部 setter 方法, 拿到方法名称的后缀, 可以大致认为其是一个 BeanName, 经过一番过滤, 然后调用 getBean 拿到对应的 Bean, 存入 PropertyValues 等到 populateBean 最后完成赋值. ByType 方式同样是根据 setter 方法, 拿到全部入参, 根据入参类型调用 BeanFactory 的 resolveDependency(@Autowired 注入的核心方法) 获取到一个合适的 Bean, 存入 PropertyValues 等到 populateBean 最后完成赋值
扩展注入, 目前有 AutowiredAnnotationBeanPostProcessor 用来处理 @Autowired 和 @Value, CommonAnnotationBeanPostProcessor 用来处理 @Resource
@Autowired @Value
@Autowired 和 @Value 的处理分为两个阶段, 首先是在 Bean 的创建流程中的 实例化 和 实例化后 之间(BeanPostProcessor), 利用反射遍历找到当前正在创建的 Bean 的类中所有需要执行注入的注入点, 就是所有被 @Autowired, @Value, @javax.inject.Inject 注解标注的字段和方法, 解析其 required 属性, 包装成为字段元素和方法元素, 然后缓存起来, 后面在依赖注入流程里直接从缓存中拿
然后在 Bean 的创建流程中的依赖注入阶段(BeanPostProcessor), 拿到缓存的注入点, 根据其是字段还是方法, 走不同的逻辑, 字段的话, 先把字段元素包装成一个依赖描述 DependencyDescriptor, 然后调用 BeanFactory.resolveDependency 先 type 后 name 的从容器中找到一个符合条件的 Bean, 完成注入. 方法的话, 遍历多个入参, 同样分别调用 resolveDependency 从容器中找到对应该入参的符合条件的 Bean, 最终将找到的 Bean 组成数组, 反射调用方法完成注入. 如果是延迟注入, 则会用代理对象代替 Bean, 在实际使用到的时候才完成查找 Bean 的任务
@Resource
同样是分两个阶段, 查找和注入, 查找也类似, 只是 @Resource 只能用在字段和单入参的方法上
同样是从缓存拿到注入点, 根据其是字段还是方法, 走不同的逻辑, 只是因为方法只有一个入参, 所以走法和字段的是一样样的. 如果 @Resource 指定了 name, 则直接用该 name 调用 getBean, 如果没有指定 name, 则拿到默认 name, 如果容器中有该 name, 则 getBean, 如果容器中没有该 name, 则调用 BeanFactory.resolveDependency 完成先 type 后 name 的查找流程, 最终完成注入. 如果是延迟注入, 则会用代理对象代替 Bean, 在实际使用到的时候才完成查找 Bean 的任务
BeanFactory.resolveDependency
用于根据依赖描述和当前正在创建的 Bean 的 BeanName 从容器中获取到同类型的候选项, 经过一番过滤排除, 剩下唯一一个满足条件的 Bean 或者 null, 返回, 完成 @Autowired / @Value 注入点的依赖注入
初始化参数名称获取器, 用于获取到入参的参数名称, 判断字段或入参有无 @Lazy 注解, 有的话生成一个代理对象, 在实际调用其方法时才真正从容器找找符合条件的 Bean, 没有 @Lazy 注解的话, 直接去容器中找 Bean
找 Bean 时首先判断该注入点是否为 @Value 注入点, 是的话, 先处理占位符, 再处理表达式, 返回处理结果即可. 然后就是处理 @Autowired 了. 主要是通过 findAutowireCandidates 方法根据类型从容器(单例池和定义池)中找出全部候选项(Bean 和 Class), 经过自引用排除, BeanDefinition.isAutowireCandidate 排除, 泛型排除, @Qualifier 排除后, 拿到符合条件的多个候选项, 再经过 @Primary 排除, @Priority 排除, 名称匹配排除, 最终拿到唯一一个符合条件的候选项, 因为可能是 Class, 所以 Class 的候选项需要经过 getBean 拿到真实的 Bean 后再返回
基础
PropertyValues
PropertyValues 用与在依赖注入阶段缓存字段和预设值的关系, 在依赖注入阶段最后统一赋值
可以手动修改 BeanDefinition 的配置来操作 Pvs: BeanDefinition.getPropertyValues.add(“orderService”, new OrderService());
通过 BeanPostProcessor 的 postProcessProperties 方法解析的依赖注入流程, 发生在预设值的统一赋值之前, 即如果 @Autowired 等赋值的字段与预设值有重复, 就会在最后统一赋值时被覆盖. 换言之, 通过 ByName 和 ByType 方式的依赖注入优先级比通过 @Autowired 等注解方式的依赖注入, 有更高的优先级
PropertyDescriptor
PropertyDescriptor 是 JDK 的内容, 解析类中的 getter 和 setter 方法, 不关心有没有对应的字段. 有任何一个, 就可以得到一个 PropertyDescriptor, 如果有一对儿, 也会被放到同一个 PropertyDescriptor 中, 一个是读方法, 一个是写方法
比如一个 Bean 里没有字段, 但有一个 setUserService 方法, 则会被解析出有 class 和 userService 两个 PropertyDescriptor, 原因是除了 setUserService, 每个对象都会有 getClass 方法, 所以只要有 getter 和 setter 中的任意一个就会被解析
Getter 方法的定义, 没有入参, (方法名以 get 开头 || (方法名以 is 开头 && 方法返回 boolean))
Setter 方法的定义, 一个入参, (方法名以 set 开头 && 返回类型为 void)
在创建 Bean 的过程中, 填充属性时, Spring 会去解析当前类, 把所有方法都拿出来, 解析每个方法并得到对应的 PropertyDescriptor 对象
- name: 并不是方法的名字, 而是根据方法名字解析得到的名字
- 以 get 开头的方法, 如 getXxx, 解析完后, name=xxx
- isXxx, name=xxx
- setXxx, name=xxx
- readMethodRef: Getter 方法的 Method 对象的引用
- readMethodName: Getter 方法的名称
- writeMethodRef: Setter 方法的 Method 对象的引用
- writeMethodName: Setter 方法的名称
- propertyTypeRef: 如果有 Getter 方法, 则就是其返回值的类型. 如果是 Setter 方法, 则就是其唯一入参的类型
DependencyDescriptor
即将注入的特定依赖项的描述符. 包装 构造函数参数/方法参数/字段, 允许统一访问其元数据(注解/类型等信息)
继承自 InjectionPoint, 提供传入字段和传入方法参数两种构造方法, 即支持注入点是字段或入参两种情况, 且两种情况互相独立, 只能选其一
方法入参和构造函数入参两种情况都归属于方法参数这一种情况
依赖注入的类型
手动注入
通过 Setter 方法手动注入
<bean name="userService" class="com.coder.main.UserService">
<property name="orderService" ref="orderService"/>
</bean>
通过构造方法手动注入
<bean name="userService" class="com.coder.main.UserService">
<constructor-arg index="0" ref="orderService"/>
</bean>
自动注入
autowire 自动注入(Spring 原生注入)
XML
在 XML 中, 可以在定义一个 Bean 时去指定自动注入模式, byType / byName / constructor / default / no, 如下示例
<bean id="userService" class="com.coder.main.UserService" autowire="byType"/>
这样写, 表示 Spring 会找到 UserService 中的所有 Setter 方法, 调用其来进行依赖注入属性赋值, 并不需要对应的属性字段上有 @Autowired 注解, 只要有 Setter 方法就行
Spring 通过 ByName 方式自动填充属性时
- 找到所有 Setter 方法对应的 Xxx 部分的名字
- 根据 Xxx 名字去容器中找 Bean
Spring 通过 ByType 方式自动填充属性时
- 找到所有 Setter 方法唯一入参的类型
- 根据类型去容器中找 Bean, 找到多个的话会报错
以上是 Spring 的 ByName 和 ByType 的情况, 而 Contructor 表示通过构造方法注入, 比较简单. Spring 利用构造方法的参数信息从容器中找 Bean, 作为参数传给构造方法, 实例化得到一个已经完成了指定字段依赖注入的 Bean 对象. 其实这种方式相当于 ByType + ByName, 普通的 ByType 是根据 Setter 方法的参数类型去找 Bean, 找到多个会报错, 而这里是通过构造方法的参数类型去找 Bean, 找到多个会根据参数名称来确定
如果有多个构造方法则需要参考推断构造方法的内容. 注意, 由于构造方法依赖注入的特殊性, 所以该种方式的依赖注入发生在 Bean 生命周期的实例化阶段而非依赖注入阶段
, 即是说, 下面讨论的依赖注入都是指 @Autowired 用于字段或 Setter 方法
no 表示关闭自动注入
default, 表示默认值, 如果 < beans> 中设置了 autowire 且 < bean> 中设置了 autowire=default, 则会使用到 < beans> 中设置的 autowire
XML 中的自动注入挺强大的, 但是 XML 毕竟已经过时了, 且现在更流行通过注解的方式来依赖注入, 而且官方都提到 @Autowired 相当于 XML 中 autowire 属性注解方式的替代, 且拥有更细的粒度 (可指定字段和方法(普通方法, 构造方法), XML 的是全部 Setter 方法) 和更广泛的适用性
Essentially, the @Autowired annotation provides the same capabilities as described in Autowiring Collaborators but with more fine-grained control and wider applicability
@Bean
@Bean(autowire = Autowire.BY_NAME), @Bean(autowire = Autowire.BY_TYPE), 和 XML 的 byType/byName 一致
@Autowired 自动注入
@Autowired 可以用在字段上, 方法上(setter方法/构造方法), 无需区分 ByType 和 ByName, 因为其内部调用 BeanFactory.resolveDependency, 该方法是先 type, 如果找到多个再根据 name 确定一个
注意, 由于构造方法依赖注入的特殊性, 所以 @Autowired 用在构造方法上这种方式的依赖注入发生在 Bean 生命周期的实例化阶段而非依赖注入阶段. 而 @Autowired 用于字段和 Setter 方法上的用法, 都是在依赖注入阶段
@Value 自动注入
和 @Autowired 同时解析与注入, 注入的时候, 经过占位符和表达式处理, 最终完成注入
@javax.inject.Inject 自动注入
基本不会使用
@Resource 自动注入
@Resource 可以用在字段和单入参的方法上, 如果指定了 name, 则直接用该 name 调用 getBean, 如果没有指定 name, 则拿到默认 name, 如果容器中有该 name, 则 getBean, 如果容器中没有该 name, 则调用 BeanFactory.resolveDependency 完成先 type 后 name 的查找流程
依赖注入的流程
依赖注入的入口在 AbstractAutowireCapableBeanFactory.doCreateBean 流程中的 AbstractAutowireCapableBeanFactory.populateBean 方法, 但是根据注入方式的不同, 完成整个依赖注入的流程也稍有不同
大致流程
- populateBean
- 实例化后, 用的很少, 执行 InstantiationAwareBeanPostProcessor 的 postProcessAfterInstantiation 方法, 虽然是放在了 populateBean 方法内部, 但也是处于方法内部的最前面, 从 createBean 的整个流程来看, 其实还是在 实例化 流程后面, 依赖注入 流程前面
- 从将要被执行依赖注入的 Bean 的 BeanDefinition 中获取到 PropertyValues, 用来缓存依赖注入的预设值, 在 populateBean 方法最后统一赋值
- 进行 Spring 原生依赖注入, 就是 ByName 和 ByType 注入
- 通过 BeanDefinition.getResolvedAutowireMode 判断注入模式是 ByName 还是 ByType
- 如果是 ByName
- 首先拿到当前 Bean 中支持依赖注入的全部字段名称(是基于 Setter 方法解析来的, 不一定能和具体字段对应上)
- 从 BeanWrapper 中拿到该类型解析出来的全部 PropertyDescriptor
- 过滤出有 Setter 方法的和非简单类型的, 以及没有被指定为忽略依赖注入的, 剩下的合并成属性名称数组
- 遍历属性名称数组, 判断有没有该名称的 Bean, 有的话执行 getBean, 并把属性名和 Bean 的印射添加到 PropertyValues
- 添加 Bean 与 Bean 的互相依赖关系印射
- 首先拿到当前 Bean 中支持依赖注入的全部字段名称(是基于 Setter 方法解析来的, 不一定能和具体字段对应上)
- 如果是 ByType
- 同样是拿到当前 Bean 中支持依赖注入的全部字段名称(是基于 Setter 方法解析来的, 不一定能和具体字段对应上)
- 遍历属性名称数组, 拿到当前属性对应的 PropertyDescriptor, 获取 Setter 方法的入参类型
- 根据入参类型调用 BeanFactory 的 resolveDependency 获取到一个合适的 Bean, 并把属性名和 Bean 的印射添加到 PropertyValues
- 添加 Bean 与 Bean 的互相依赖关系印射
- 进行 Spring 扩展依赖注入, @Autowired, @Value, @Resource 等就是在这里完成注入
- 执行 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法, 传入了 PropertyValues, 自行决定如何使用
- AutowiredAnnotationBeanPostProcessor 处理 @Autowired, @Value
- @Autowired 注解的字段经过反射赋值, 注解的方法经过反射调用, 注解的构造方法估计是在实例化时调用
- 大致流程是根据需要的类型找到符合条件的候选项, 然后经过一系列条件过滤(名称过滤是保底手段, 一定能从多个里过滤出唯一一个或者 null), 将唯一的候选项返回, 就是字段/入参的值
- CommonAnnotationBeanPostProcessor, 处理 @Resource
- AutowiredAnnotationBeanPostProcessor 处理 @Autowired, @Value
- 执行 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法, 传入了 PropertyValues, 自行决定如何使用
- 遍历 PropertyValues 执行统一赋值, 如果和扩展依赖注入有重复项, 会在这里覆盖, 可见原生依赖注入(XML autowire=byType/byName)的优先级更高
@Autowired 解析与注入
AutowiredAnnotationBeanPostProcessor 负责处理的是 @Autowired, @Value, @javax.inject.Inject 三个注解
@Autowired 的注入是在 依赖注入 流程中完成, 但是解析不是, 解析的入口是在 createBean 的 实例化 流程之后, 实例化后 流程之前, 是通过 BeanPostProcessor 来提供的, 执行 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition 方法
解析的大致流程
AutowiredAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 接口, 其 postProcessMergedBeanDefinition 方法用来解析寻找依赖的注入点(就是需要执行注入的字段和方法)并缓存起来, 在后面 populateBean 里直接拿缓存结果, 走找 Bean 赋值的流程
- 判断当前 Bean 的类型是否为需要解析与注入, 如果类型的全限定名以 “java.” 开头, 则不进行解析, 因为以此开头的都是 JDK 中的类, 不会有 @Autowired 等注解了字段和方法的情况. 比如如果一个 Bean 的类型是 String, 则其内部肯定没有其他的需要注入的 Bean, 所以就不需要解析
- 反射获取类的全部字段并遍历
- 如果字段上有这三个注解中的任何一个, 则该字段就会被拿到
- 静态字段会被过滤掉, 因为静态字段属于类而非对象, 任何一次修改都会对所有的该类型的 Bean 造成影响
- 解析该注解的 required 属性, required=true 时, 在依赖注入时如果没找到 Bean 则会报错
- 将找到的字段和 required 包装成 AutowiredFieldElement添加到集合中
- 反射获取类的全部方法并遍历
- 如果方法是桥接方法(和泛型与泛型擦除有关), 则找到与之对应的普通方法, 然后继续
- 如果方法上有这三个注解中的任何一个, 则该方法就会被拿到
- 静态方法会被过滤掉
- 如果方法没有入参, 则会打印一条日志
- Spring 不限制 @Autowired 方法的参数的个数, 参数有几个都会被找 Bean, 即使没有入参也会被认为是注入点, Spring 还是会调用该方法. 所以如果想在依赖注入阶段让某个 Bean 的没有参数的方法被调用, 可以加一个 @Autowired 注解
- 解析该注解的 required 属性
- 将找到的字段和 required 包装成 AutowiredMethodElement 添加到集合中
- 递归获取父类需要注入的字段和方法, 然后插入到集合的最前面
- 将找出来的需要自动注入的字段和方法打包成一个 InjectionMetadata 对象
- 将之缓存起来
AutowiredFieldElement 和 AutowiredMethodElement 都是 AutowiredElement 的子类, 覆盖了其 inject 方法, 后续注入时要注意使用的是子类
注入的大致流程
SmartInstantiationAwareBeanPostProcessor 接继承了 InstantiationAwareBeanPostProcessor 接口, 扩展依赖注入就是执行 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法来完成的
AutowiredAnnotationBeanPostProcessor 实现了 SmartInstantiationAwareBeanPostProcessor 接口, 其 postProcessProperties 方法用来找到 Bean 并完成依赖注入
- 从缓存里找到之前 postProcessMergedBeanDefinition 方法找到并缓存的注入点, 注入点包括字段和方法两种类型
- 遍历每个 InjectedElement 元素, 执行其 inject 方法来注入, 这里要看 AutowiredFieldElement 和 AutowiredMethodElement
- 如果是字段
- 根据该字段的类型和名称等信息, 调用 BeanFactory 的 resolveDependency 方法去找 Bean
- 然后利用反射技术直接赋值
- 如果是方法
- 判断 PropertyValues 中是否已经有当前注入点的值了, 如果有则跳过本次注入
- 根据方法的全部入参的类型和名称去找对应的 Bean 的数组
- 然后反射执行该方法来赋值
- 不管是字段还是方法, 都是主要调用 BeanFactory 的 resolveDependency 方法来寻找合适的 Bean
resolveDependency (核心)
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException
该方法作用, 根据传入的依赖描述(DependencyDescriptor) 从 BeanFactory 中找出对应的唯一的一个 Bean 对象
入口在 DefaultListableBeanFactory.resolveDependency, 用于根据要注入的字段/入参的要求, 从容器里找到符合条件的依赖对象
- 初始化参数名称获取器, 标准反射参数名称发现器 和 本地变量表参数名称发现器, 用于拿到方法入参的参数名(BeanName)
- 如果编译参数有 -parameter, 则通过反射即可拿到入参参数名称, 否则只能通过本地变量表的方式拿到入参参数名称
- 处理注入的依赖类型是 Optional 的情况
- 处理注入的依赖类型是 ObjectProvider 和 ObjectFactory 的情况
- 处理注入的依赖类型是 javax.inject.Provider 的情况
- 处理剩下的情况, 即我们常规使用的注入方式
- 首先判断有无 @Lazy (可用在字段上, 可用在方法入参前面, 也可以用在类上, 但暂不考虑这种情况)
- 如果是延迟注入, 则返回的对象是一个代理对象, 实际调用该代理对象的具体方法时, 才会真正去调用 doResolveDependency 方法去解析拿到真正的 Bean
- 如果没有拿到对象, 说明不是延迟注入, 就会真正执行 doResolveDependency, 先根据 ByType 找到候选项, 经过多次过滤, 再根据 ByName 确认唯一的候选项
doResolveDependency
入口在 **DefaultListableBeanFactory.doResolveDependency **
- 缓存机制, 如果之前依赖描述对象已经做过依赖解析了(有要获取的 BeanName 和 required), 则这里直接调用 getBean(name, required) 返回就行
- 处理 @Value 注解
- @Value 可以用在字段上, 也可以用在方法入参前面, 也可以用在构造函数入参前面, 判断该依赖描述对象所代表的注入点(字段/入参)是否有 @Value 注解, 有的话获取其 value 值
- 处理占位符 @Value(“${}”), 占位符会从 Environment(环境变量/运行参数-D/properties文件等) 中获取值, 有优先级(-D 最高)
- 处理表达式 @Value(“#{}”), Spring 表达式非常强大, 可以是根据名称找 Bean, 如 @Value(“#{userService}”), 也可以是其他
- 处理 @Autowired 注解
- 处理注入类型是批量对象的情况, 如 Array/Collection/Map 等, 通过调用 findAutowireCandidates 获取到符合类型条件的候选项, 返回的是以 BeanName 为 Key, Bean(有可能是 Class) 为 Value 的 Map, 根据要求的类型返回合适的类型
- 处理注入类型是普通对象的情况, 即我们常规使用的注入方式
- 首先调用 findAutowireCandidates 拿到所有符合条件的候选项的 Map
- 如果没有找到, 判断 required 条件, 如果 required, 报错, 如果非 required, 返回 null
- 如果找到的候选项不止一个, 则需要做一些过滤, 最终拿到唯一一个候选项
- 如果候选项里只有一个有 @Primary 注解, 则直接认定这个就是最终候选项, 直接返回, 如果有多个 @Primary, 则报错
- 如果候选项里有 @Priority 注解(该注解只能用在类上), 则找到优先级最高的那个候选项, 直接返回, 如果有多个同级的, 则报错
- 如某接口有两个实现类, 现在某 Bean 有一个类型是该接口的字段, 则会判断两个实现类上的 @Priority 注解的 value, 越小优先级越高
- 最终的后备手段, ByName 匹配, 当前需要注入的字段/入参的名称为依赖名称, 当依赖名称和候选项名称相同, 或依赖名称后候选项名称的某个别名相同, 即可认为 ByName 匹配成功
- 如果都排除掉了, 判断 required 条件, 如果 required, 报错, 如果非 required, 返回 null
- 如果找到的候选项只有一个, 则直接将这一个作为最终的唯一一个
- 确定的唯一一个候选项可能是个 Class 对象, 这种情况就得去 getBean 一下了
- 为什么可能是 Class, 因为创建某个 Bean 的时候, 其依赖的 Bean 可能还没有创建, 即还在定义池中, 从定义池里找依赖的时候, 就会把 BeanDefinition 的 beanClass 从全限定类名解析成为 Class, 即从定义池中拿到的候选, 就是一个 Class 对象
- 如果唯一一个候选项是一个 NullBean, 则报错
- 如果一个 Bean 创建了一个 null, 如 @Bean 返回 null, 则会使用 NullBean 代替, 保证单例池中没有 null
- 返回最终找到的唯一一个 Bean
findAutowireCandidates
findAutowireCandidates
根据类型找 BeanName 的底层流程 doGetBeanNamesForType
入口在 DefaultListableBeanFactory.findAutowireCandidates
- 根据需求的类型, 从单例池和定义池, 遍历判断类型, 将符合条件的, 其 BeanName 添加到候选项名称结果集, 最终返回
- 在定义池中遍历 BeanDefinition, 判断类型是否匹配, 如果是 FactoryBean, 则会调用其 getObjectType, 用返回的类型做匹配
- 定义一个 Map<String, Object> 的结果集, 用于存放符合要求的 BeanName 和 候选项(不一定是 Bean, 也可能是 Class)
- 根据需求的类型从 resolvableDependencies 中找出一些符合要求的候选项, 加到结果集中
- 遍历之前根据类型找出的候选项名称结果集, 找出其中符合条件的候选项, 加到结果集中
- 排除自引用的候选项(如果最终候选项为空, 还得把自引用加回去)
- 举例, User 类有一个 User 字段需要依赖注入, User 类上有 @Component 注解, 额外还有一个 @Bean 的 User, 默认注入的就是 @Bean 的这个 User, 而不是 @Component 的这个 User, 即使 BeanName 不一致也没关系, 因为自引用的没得选了才考虑
- 排除不支持被注入的候选项, BeanDefinition.isAutowireCandidate=false
- 排除和 Bean 的泛型字段/入参不匹配的候选项, 判断正在注入的字段/方法是否是泛型的, 获取真实类型, 和当前候选项是否匹配
- 判断当前 type 是不是泛型, 如果是泛型, 则会把容器中所有的 BeanName 找出来的, 如果是这种情况, 那么在这一步中就要获取到泛型的真正类型, 然后进行匹配, 如果当前 BeanName 和当前泛型对应的真实类型匹配, 那么则继续判断
- 排除和有 @Qualifier 注解的 DepencencyDescriptor 不匹配的候选项, 如果 DepencencyDescriptor 上有 @Qualifier, 则候选项上也必须得有同值的 @Qualifier 注解, 两者才算是匹配的
- 排除自引用的候选项(如果最终候选项为空, 还得把自引用加回去)
- 如果最后结果集是空的, 则尝试把找出来的自引用 Bean 加入到结果集中(有自引用才会在这里加入)
- 最终返回结果集, 通过反射的方式执行字段/入参的相关方法, 完成依赖注入
@Resource 解析与注入
和 @Autowired 的流程相似, 都是先找到注入点, 再注入, 同样也公用了 BeanFactory.resolveDependency 方法
解析的大致流程
CommonAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 接口, 其 postProcessMergedBeanDefinition 方法用来解析寻找依赖的注入点(就是需要执行注入的字段和方法)并缓存起来, 在后面 populateBean 里直接拿缓存结果, 走找 Bean 赋值的流程
遍历字段和方法, 寻找有 @WebServiceRef, @EJB, @Resource 注解的, 作为注入点, 包装成 ResourceElement 并缓存起来, 注意: @Resource 用在方法上时只能是单参数的方法
只考虑 @Resource 就可以了, 另外两种基本没用
注入的大致流程
CommonAnnotationBeanPostProcessor 实现了 InstantiationAwareBeanPostProcessor 接口, 其 postProcessProperties 方法用来找到 Bean 并完成依赖注入
从缓存中拿到注入点, 遍历 ResourceElement 执行 inject 方法, 注意 ResourceElement 的 inject 方法继承自其父父类 InjectionMetadata.
- 如果是字段
- 调用 ResourceElement.getResourceToInject 方法, 如果有 @Lazy 注解, 则生成代理对象并返回完成注入, 代理对象在 getTarget 的时候会去获取对应 Bean, 如果非延迟, 则直接去获取对应 Bean
- 如果 @Resource 没有指定 name, 且容器中不包含默认名称的 Bean, 就会调用 BeanFactory.resolveDependency, 先根据类型去找, 做一些过滤排除, 如果有多个的话再根据名称筛选, 确保唯一
- element.isDefaultName, new ResourceElement 的时候就会解析得到该值, @Resource 没有指定 name 时, 该值为 true
- 当没有指定 name 时, element.name 就是默认解析的名字, 是 字段名 或 方法名除去 set 前缀后首字母小写得到的名字
- 容器中不包含默认名称的 Bean, 指的就是这个默认名称对应的 Bean
- 通过 factory.containsBean(name) 来判断容器中是否包含某个名称的 Bean
- 如果 @Resource 没有指定 name, 且容器中包含默认名称的 Bean, 就会走 getBean 来直接获取
- 如果 @Resource 指定了 name, 直接走 getBean 来获取
- 如果是方法
- 判断当前正在创建的 Bean 的 PropertyValues 里面有没有该字段/入参, 有的话就跳过
- 其他的和处理字段的流程一样(@Resource 只能用在单入参的方法上)