依赖注入是 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 的处理分为两个阶段, 首先是在 Bean 的创建流程中的 实例化 和 实例化后 之间(BeanPostProcessor), 利用反射遍历找到当前正在创建的 Bean 的类中所有需要执行注入的注入点, 就是所有被 @Autowired, @Value, @javax.inject.Inject 注解标注的字段和方法, 解析其 required 属性, 包装成为字段元素和方法元素, 然后缓存起来, 后面在依赖注入流程里直接从缓存中拿
然后在 Bean 的创建流程中的依赖注入阶段(BeanPostProcessor), 拿到缓存的注入点, 根据其是字段还是方法, 走不同的逻辑, 字段的话, 先把字段元素包装成一个依赖描述 DependencyDescriptor, 然后调用 BeanFactory.resolveDependency 先 type 后 name 的从容器中找到一个符合条件的 Bean, 完成注入. 方法的话, 遍历多个入参, 同样分别调用 resolveDependency 从容器中找到对应该入参的符合条件的 Bean, 最终将找到的 Bean 组成数组, 反射调用方法完成注入. 如果是延迟注入, 则会用代理对象代替 Bean, 在实际使用到的时候才完成查找 Bean 的任务
同样是分两个阶段, 查找和注入, 查找也类似, 只是 @Resource 只能用在字段和单入参的方法上
同样是从缓存拿到注入点, 根据其是字段还是方法, 走不同的逻辑, 只是因为方法只有一个入参, 所以走法和字段的是一样样的. 如果 @Resource 指定了 name, 则直接用该 name 调用 getBean, 如果没有指定 name, 则拿到默认 name, 如果容器中有该 name, 则 getBean, 如果容器中没有该 name, 则调用 BeanFactory.resolveDependency 完成先 type 后 name 的查找流程, 最终完成注入. 如果是延迟注入, 则会用代理对象代替 Bean, 在实际使用到的时候才完成查找 Bean 的任务
用于根据依赖描述和当前正在创建的 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 用与在依赖注入阶段缓存字段和预设值的关系, 在依赖注入阶段最后统一赋值
可以手动修改 BeanDefinition 的配置来操作 Pvs: BeanDefinition.getPropertyValues.add(“orderService”, new OrderService());
通过 BeanPostProcessor 的 postProcessProperties 方法解析的依赖注入流程, 发生在预设值的统一赋值之前, 即如果 @Autowired 等赋值的字段与预设值有重复, 就会在最后统一赋值时被覆盖. 换言之, 通过 ByName 和 ByType 方式的依赖注入优先级比通过 @Autowired 等注解方式的依赖注入, 有更高的优先级
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 对象
即将注入的特定依赖项的描述符. 包装 构造函数参数/方法参数/字段, 允许统一访问其元数据(注解/类型等信息)
继承自 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>
在 XML 中, 可以在定义一个 Bean 时去指定自动注入模式, byType / byName / constructor / default / no, 如下示例
<bean id="userService" class="com.coder.main.UserService" autowire="byType"/>
这样写, 表示 Spring 会找到 UserService 中的所有 Setter 方法, 调用其来进行依赖注入属性赋值, 并不需要对应的属性字段上有 @Autowired 注解, 只要有 Setter 方法就行
Spring 通过 ByName 方式自动填充属性时
Spring 通过 ByType 方式自动填充属性时
以上是 Spring 的 ByName 和 ByType 的情况, 而 Contructor 表示通过构造方法注入, 比较简单. Spring 利用构造方法的参数信息从容器中找 Bean, 作为参数传给构造方法, 实例化得到一个已经完成了指定字段依赖注入的 Bean 对象. 其实这种方式相当于 ByType + ByName, 普通的 ByType 是根据 Setter 方法的参数类型去找 Bean, 找到多个会报错, 而这里是通过构造方法的参数类型去找 Bean, 找到多个会根据参数名称来确定
如果有多个构造方法则需要参考推断构造方法的内容. 注意, 由于构造方法依赖注入的特殊性, 所以该种方式的依赖注入发生在 Bean 生命周期的实例化阶段而非依赖注入阶段
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(autowire = Autowire.BY_NAME), @Bean(autowire = Autowire.BY_TYPE), 和 XML 的 byType/byName 一致
@Autowired 可以用在字段上, 方法上, 无需区分 ByType 和 ByName, 因为其内部调用 BeanFactory.resolveDependency, 该方法是先 type, 如果找到多个再 name 确定一个
和 @Autowired 同时解析与注入, 注入的时候, 经过占位符和表达式处理, 最终完成注入
基本不会使用
@Resource 可以用在字段和单入参的方法上, 如果指定了 name, 则直接用该 name 调用 getBean, 如果没有指定 name, 则拿到默认 name, 如果容器中有该 name, 则 getBean, 如果容器中没有该 name, 则调用 BeanFactory.resolveDependency 完成先 type 后 name 的查找流程
依赖注入的入口在 AbstractAutowireCapableBeanFactory.doCreateBean 流程中的 AbstractAutowireCapableBeanFactory.populateBean 方法, 但是根据注入方式的不同, 完成整个依赖注入的流程也稍有不同
@Autowired 依赖注入 大致流程
AutowiredAnnotationBeanPostProcessor 负责处理的是 @Autowired, @Value, @javax.inject.Inject 三个注解
@Autowired 的注入是在 依赖注入 流程中完成, 但是解析不是, 解析的入口是在 createBean 的 实例化 流程之后, 实例化后 流程之前, 是通过 BeanPostProcessor 来提供的, 执行 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition 方法
AutowiredAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 接口, 其 postProcessMergedBeanDefinition 方法用来解析寻找依赖的注入点(就是需要执行注入的字段和方法)并缓存起来, 在后面 populateBean 里直接拿缓存结果, 走找 Bean 赋值的流程
AutowiredFieldElement 和 AutowiredMethodElement 都是 AutowiredElement 的子类, 覆盖了其 inject 方法, 后续注入时要注意使用的是子类
SmartInstantiationAwareBeanPostProcessor 接继承了 InstantiationAwareBeanPostProcessor 接口, 扩展依赖注入就是执行 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法来完成的
AutowiredAnnotationBeanPostProcessor 实现了 SmartInstantiationAwareBeanPostProcessor 接口, 其 postProcessProperties 方法用来找到 Bean 并完成依赖注入
resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException
该方法作用, 根据传入的依赖描述(DependencyDescriptor) 从 BeanFactory 中找出对应的唯一的一个 Bean 对象
入口在 DefaultListableBeanFactory.resolveDependency, 用于根据要注入的字段/入参的要求, 从容器里找到符合条件的依赖对象
入口在 **DefaultListableBeanFactory.doResolveDependency **
findAutowireCandidates
根据类型找 BeanName 的底层流程 doGetBeanNamesForType
入口在 DefaultListableBeanFactory.findAutowireCandidates
大致流程
和 @Autowired 的流程相似, 都是先找到注入点, 再注入, 同样也公用了 BeanFactory.resolveDependency 方法
CommonAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 接口, 其 postProcessMergedBeanDefinition 方法用来解析寻找依赖的注入点(就是需要执行注入的字段和方法)并缓存起来, 在后面 populateBean 里直接拿缓存结果, 走找 Bean 赋值的流程
遍历字段和方法, 寻找有 @WebServiceRef, @EJB, @Resource 注解的, 作为注入点, 包装成 ResourceElement 并缓存起来, 注意: @Resource 只能用在单入参的方法上
只考虑 @Resource 就可以了, 另外两种基本没用
CommonAnnotationBeanPostProcessor 实现了 InstantiationAwareBeanPostProcessor 接口, 其 postProcessProperties 方法用来找到 Bean 并完成依赖注入
从缓存中拿到注入点, 遍历 ResourceElement 执行 inject 方法, 注意 ResourceElement 的 inject 方法继承自其父父类 InjectionMetadata.