这次彻底搞懂IoC容器依赖注入的源码(下)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 初始化的过程,主要完成的工作是在容器中建立 BeanDefinition 数据映射,并没有看到容器对Bean依赖关系进行注入。 假设当前IoC容器已经载入用户定义的Bean信息,依赖注入主要发生在两个阶段 正常情况下,由用户第一次向 IoC 容器索要 bean 时触发 可在 BeanDefinition 信息中通过控制 lazy-init 属性 来让容器完成对Bean的预实例化,即在初始化的过程中就完成某些Bean的依赖注入的过程。

使用CGLIB来生成Bean对象

需要看一下SimpleInstantiationStrategy类;

它是 Spring 用来生成Bean对象的默认类;

它提供了两种实例化Bean对象的方法

  • BeanUtils,使用Java原生的反射功能
  • CGLIB
public class SimpleInstantiationStrategy implements InstantiationStrategy {
    @Override
  public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
    // Don't override the class with CGLIB if no overrides.
    if (bd.getMethodOverrides().isEmpty()) {
            //这里取得指定的构造器或者生成对象的工厂方法来对Bean进行实例化
      Constructor<?> constructorToUse;
      synchronized (bd.constructorArgumentLock) {
        constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
        if (constructorToUse == null) {
          final Class<?> clazz = bd.getBeanClass();
          if (clazz.isInterface()) {
            throw new BeanInstantiationException(clazz, "Specified class is an interface");
          }
          try {
            if (System.getSecurityManager() != null) {
              constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                @Override
                public Constructor<?> run() throws Exception {
                  return clazz.getDeclaredConstructor((Class[]) null);
                }
              });
            }
            else {
              constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
            }
            bd.resolvedConstructorOrFactoryMethod = constructorToUse;
          }
          catch (Throwable ex) {
            throw new BeanInstantiationException(clazz, "No default constructor found", ex);
          }
        }
      }
            //通过BeanUtils进行实例化,这个BeanUtils的实例化通过Constructor来实例化Bean,在BeanUtils中可以看到具体的调用ctor.newInstance(args)
      return BeanUtils.instantiateClass(constructorToUse);
    }
    else {    
            // 使用CGLIB来实例化对象
      return instantiateWithMethodInjection(bd, beanName, owner);
    }
  }
}

4 Bean之间依赖关系的处理

依赖关系处理的入口是前面提到的populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    // 尝试从BeanDefinition中获取PropertyValue的属性集合,还没有值
    PropertyValues pvs = mbd.getPropertyValues();
    // 设置上面的PropertyValues的值,默认是getResolvedAutowiredMode方法返回0
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
      mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }
        if (hasInstAwareBpps) {
            // Autowired
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = 
                                                (InstantiationAwareBeanPostProcessor) bp;
                    pvs = ibp.postProcessPropertyValues(//瞅到没,这个方法哦~~~
                                    pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }
    applyPropertyValues(beanName, mbd, bw, pvs);
}

由其涉及面广,仅简要介绍依赖关系处理的流程:

在populateBean方法中


首先取得在BeanDefinition中设置的property值,然后开始依赖注入的过程

首先处理Autowire的注入,可以by Name/Type,之后对属性进行注入

接着需要对Bean Reference进行解析

在对ManageList、ManageSet、ManageMap等进行解析完之后,就已经为依赖注入准备好了条件;

这是真正把Bean对象设置到它所依赖的另一个Bean属性中去的地方.

依赖注入发生在BeanWrapper的setPropertyValues中

具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现;

它会完成Bean的属性值的注入,其中包括对Array的注入、对List等集合类以及对非集合类的域进行注入

经过一系列的注入,这样就完成了对各种Bean属性的依赖注入过程


在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入


从前面的几个递归过程中可以看到,这些递归都是以getBean为入口


一个递归是在上下文中查找需要的Bean和创建Bean的递归调用

另一个递归是在依赖注入时,通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。

在对Bean的属性进行依赖注入时,解析也是一个递归的过程

根据依赖关系,层层完成Bean的创建和注入,直到最后完成当前Bean的创建

有了这个顶层Bean的创建和对它属性依赖注入的完成,意味着和当前Bean相关的整个依赖链的注入也就完成了


在Bean创建和依赖注入完成后,在容器中建立起一系列依靠依赖关系联系起来的Bean,这个Bean已经不再是简单的Java对象了;

该Bean系列以及Bean之间的依赖关系建立完成之后,通过IoC的相关接口方法,就可以非常方便地供上层应用使用了。

5 lazy-init属性和预实例化

在前面的refresh中,可看到调用了finishBeanFactoryInitialization来对配置了lazy-init的Bean进行处理

其实在这个方法中,封装了对lazy-init属性的处理,实际的处理是在DefaultListableBeanFactory这个基本容器的preInstantiateSingleton方法中完成的

该方法对单例Bean完成预实例化,这个预实例化的完成巧妙地委托给了容器来实现

如果需要预实例化,那么就直接在这里采用getBean去触发依赖注入


目录
相关文章
|
1月前
|
Java 测试技术 开发工具
ApplicationArguments读取应用程序参数并注入到IOC容器
ApplicationArguments读取应用程序参数并注入到IOC容器
ApplicationArguments读取应用程序参数并注入到IOC容器
|
2月前
|
XML Java 数据格式
Spring IoC容器初始化过程(xml形式)
Spring IoC容器初始化过程(xml形式)
46 0
|
1月前
|
存储 前端开发 Java
springboot中的第二个IOC容器BootstrapContext
springboot中的第二个IOC容器BootstrapContext
springboot中的第二个IOC容器BootstrapContext
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
|
3月前
|
XML Java 数据格式
Spring5源码(26)-ApplicationContext容器refresh过程简析
Spring5源码(26)-ApplicationContext容器refresh过程简析
38 0
|
3月前
|
XML Java 数据格式
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
31 1
|
1月前
|
Java Go 开发者
Docker容器技术简介及其与Go语言的结合点
【2月更文挑战第23天】本文首先概述了Docker容器技术的核心概念和优势,接着探讨了Go语言与Docker容器技术的结合点。通过阐述Docker的轻量级、可移植性和版本控制等特性,以及Go语言在容器化应用中的优势,本文旨在说明两者结合能够实现更高效、灵活的应用开发和部署。
|
6天前
|
存储 运维 监控
构建高效稳定的Docker容器监控体系
【4月更文挑战第18天】 在现代微服务架构中,Docker容器已成为部署和运行应用的标准环境。随之而来的挑战是如何有效监控这些容器的性能与健康状况,确保系统的稳定性和可靠性。本文将探讨构建一个高效稳定的Docker容器监控体系的关键技术和方法,包括日志管理、性能指标收集以及异常检测机制,旨在为运维人员提供实用的指导和建议。
12 0
|
15天前
|
Linux Docker 容器
docker 容器常用命令
docker 容器常用命令
13 0