【老王读Spring IoC-4】Spring IoC之依赖注入的原理

IoC之依赖注入

  • 前言
  • 版本约定
  • 正文
    • 解析依赖的详细逻辑----doResolveDependency
  • 总结
  • SpringIoC源码视频讲解:

前言

前面我们分析了 IoC 之 BeanDefinition 的扫描注册,那么接下来要解决的问题就是依赖关系的注入。

通过 IoC之BeanDefinition扫描注册 的分析,我们知道 BeanDefinition 注册阶段,bean 的实例是没有产生的,
它只是将 BeanDefinition 注册到了 BeanDefinitionRegistry 中。

那么 bean 依赖的属性又是在什么时候注入到 bean 的实例中的呢?

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

前面分析 BeanDefinition注册 时,我们知道依赖的注入是通过 InjectionMetadata.InjectedElement#inject() 来完成的。那么,我们就以这个点做为突破口。
AutowiredFieldElement 是来处理 @Autowired 属性类型注入的,所以,我们将断点打在 AutowiredFieldElement#inject() 上。
【老王读Spring IoC-4】Spring IoC之依赖注入的原理_第1张图片

更多 InjectedElement 的知识请移步 @Resource与@Autowired的区别

我们可以看到,依赖的注入发生在 AbstractApplicationContext#finishBeanFactoryInitialization() 阶段,进行 populateBean 的时候。

查看 DefaultListableBeanFactory#preInstantiateSingletons() 的源码可以发现,Spring 会去循环所有的 beanDefinitionNames,逐个调用 getBean() 来初始化 bean。

// DefaultListableBeanFactory#preInstantiateSingletons()
public void preInstantiateSingletons() throws BeansException {
    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                                (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    } else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            } else {
                getBean(beanName);
            }
        }
    }
......
}

从调用堆栈中,我们可以看到 inject() 之前做了两个重要的动作:doCreateBean() 和 populateBean()

doCreateBean(): 创建 bean 的实例
populateBean(): 填充 bean 依赖的属性

所以,依赖的注入是发生在创建 bean 的实例后,为 bean 的实例填充属性的时候(populateBean)。

AutowiredFieldElement#inject() 的代码比较简单,最关键的代码就是从 beanFactory 中将依赖解析出来:

Object value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

【老王读Spring IoC-4】Spring IoC之依赖注入的原理_第2张图片

解析依赖的详细逻辑----doResolveDependency

解析依赖的详细逻辑在 DefaultListableBeanFactory#doResolveDependency() 中:
【老王读Spring IoC-4】Spring IoC之依赖注入的原理_第3张图片

DependencyDescriptor#resolveCandidate() 方法会触发依赖 bean 的加载动作,里面会调用 BeanFactory#getBean(),从而触发依赖 bean 的加载流程。

// DependencyDescriptor#resolveCandidate()
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
    throws BeansException {
    return beanFactory.getBean(beanName);
}

依赖解析时的逻辑和顺序如下:

  1. 是否是 @Value 类型的依赖
  2. 是否是 Collection、Array、Map 等容器对象的依赖
  3. 是否是 resolvableDependency 类型的依赖
  4. 以上都不是,就会走普通依赖的注入流程 --> DependencyDescriptor#resolveCandidate()

普通依赖的注入流程也就是 getBean() 的流程

可以看出,Spring 能处理的依赖类型有多种

  1. @Value 类型的依赖
  2. Collection、Array、Map 等容器对象的依赖
  3. resolvableDependency 类型的依赖
  4. 普通 bean 的依赖

@Value 也是做为一种依赖类型在处理的。
也就是说: bean 里面被 @Value、@Component、@Autowired、@Resource 标记的属性都会被当作依赖进行注入。
划重点:依赖注入的过程,可以认为是 bean 中属性填充的过程。

总结

Spring 对依赖的注入 AbstractApplicationContext#finishBeanFactoryInitialization 阶段。
这个时候,会遍历所有的 beanDefinitionNames,为每一个 BeanDefinition 去创建实例。
创建 BeanDefinition 的实例时,会先实例化 bean,然后再为 bean 的实例填充属性(populateBean),填充属性的过程也就是依赖注入的过程。
具体的注入动作是由 InjectionMetadata.InjectedElement#inject() 来完成的。

依赖解析时的逻辑和顺序如下:

  1. 是否是 @Value 类型的依赖
  2. 是否是 Collection、Array、Map 等容器对象的依赖
  3. 是否是 resolvableDependency 类型的依赖
  4. 以上都不是,就会走普通依赖的注入流程 --> DependencyDescriptor#resolveCandidate()

SpringIoC源码视频讲解:

课程 地址
SpringIoC源码解读由浅入深 https://edu.51cto.com/sd/68e86

如果本文对你有所帮助,欢迎点赞收藏!

源码测试工程下载:
老王读Spring IoC源码分析&测试代码下载
老王读Spring AOP源码分析&测试代码下载

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
gzh


系列博文:
【老王读Spring IoC-0】Spring IoC 引入
【老王读Spring IoC-1】IoC 之控制反转引入
【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册
【老王读Spring IoC-3】Spring bean 的创建过程
【老王读Spring IoC-4】IoC 之依赖注入原理
【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入

相关阅读:
【Spring源码三千问】@Resource 与 @Autowired 的区别
【Spring源码三千问】bean name 的生成规则
【Spring源码三千问】BeanDefinition详细分析
【Spring源码三千问】Spring 是怎样解决循环依赖问题的?
【Spring源码三千问】哪些循环依赖问题Spring解决不了?
【Spring源码三千问】@Lazy为什么可以解决特殊的循环依赖问题?
【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?
【Spring源码三千问】Bean的Scope有哪些?scope=request是什么原理?
【Spring源码三千问】为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?

你可能感兴趣的:(#,Spring,IoC源码分析,spring,java)