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

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

初始化的过程,主要完成的工作是在容器中建立 BeanDefinition 数据映射,并没有看到容器对Bean依赖关系进行注入。


假设当前IoC容器已经载入用户定义的Bean信息,依赖注入主要发生在两个阶段


正常情况下,由用户第一次向 IoC 容器索要 bean 时触发

可在 BeanDefinition 信息中通过控制 lazy-init 属性

image.png

  • 来让容器完成对Bean的预实例化,即在初始化的过程中就完成某些Bean的依赖注入的过程。

1 getBean触发的依赖注入

BeanFactory,最原始的 IoC 容器,有如下方法

  1. getBean
  2. 判断是否有 Bean,containsBean
  3. 判断是否单例 isSingleton

BeanFactory 只对 IoC 容器最基本的行为作了定义,不关心 Bean 是怎样定义和加载的。如果我们想知道一个工厂具体产生对象的过程,则要看这个接口的实现类。


在基本的容器接口 BeanFactory 中,有一个 getBean接口,该接口的实现就是触发依赖注入发生的地方。为进一步了解该依赖注入过程,从 DefaultListableBeanFactory的基类AbstractBeanFactory入手看看getBean的实现。


这里是对 BeanFactory 接口的实现,比如getBean接口。

public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
       // 这些 getBean 接口最终都是通过调用 doGetBean 实现
  return doGetBean(name, requiredType, args, false);
}

返回一个实例,该实例可以是指定bean的共享或独立的

// 实际取得 Bean 的地方,即触发依赖注入
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name,
              final Class<T> requiredType,
              final Object[] args,
              boolean typeCheckOnly) {
  // 去掉工厂bean的前缀或者将别名转化为规范名
  final String beanName = transformedBeanName(name);
  Object bean;
    // 检查是否有已注册的bean实例
    // 急切地检查单例模式缓存手动注册的单例
    //先从缓存中取得Bean,处理那些已经被创建过的单例Bean,这种Bean不要重复创建
  Object sharedInstance = getSingleton(beanName);

getSingleton()

返回以给定名称注册的(原始)单例对象。

仅检查已经实例化的单例; 对于尚未实例化的单例bean定义,不返回Object。

此方法的主要目的是访问手动注册的单例(请参见registerSingleton )。 也可以用于以原始方式访问由已创建的bean定义定义的单例。

注意:此查找方法不知道FactoryBean前缀或别名。 在获取单例实例之前,您需要首先解析规范化的bean名称

image.png

protected Object getSingleton(String beanName,
                boolean allowEarlyReference) {
  // 获取一级缓存中的值              
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    // 同步保证线程安全
    synchronized (this.singletonObjects) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      // 第一次创建时,当然是空
      if (singletonObject == null && allowEarlyReference) {
        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
        if (singletonFactory != null) {
          // 先从一级缓存获取
          singletonObject = singletonFactory.getObject();
          // 并将其加入到二级缓存
          this.earlySingletonObjects.put(beanName, singletonObject);
          // 从三级缓存删除
          // 这样就从三级缓存升级到二级缓存了
          this.singletonFactories.remove(beanName);
        }
      }
    }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

回过头继续看工厂实现

if (sharedInstance != null && args == null) {
          ...
          //以取得 FactoryBean 的相关处理及生产结果
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

来看看该 getObjectForBeanInstance 方法的实现,和FactoryBean有何关系呢

image.png

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
  // Don't let calling code try to dereference the factory if the bean isn't a factory.
  if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
    throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
  }
  // Now we have the bean instance, which may be a normal bean or a FactoryBean.
  // If it's a FactoryBean, we use it to create a bean instance, unless the
  // caller actually wants a reference to the factory.
  if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
    return beanInstance;
  }
  Object object = null;
  if (mbd == null) {
    object = getCachedObjectForFactoryBean(beanName);
  }
  if (object == null) {
    // Return bean instance from factory.
    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    // Caches object obtained from FactoryBean if it is a singleton.
    if (mbd == null && containsBeanDefinition(beanName)) {
      mbd = getMergedLocalBeanDefinition(beanName);
    }
    boolean synthetic = (mbd != null && mbd.isSynthetic());
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
  }
  return object;
}

继续回过头

  else {
    // 若早已创建该 bean 实例则会失败进入到此
    // 假设处在引用循环依赖中
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
           // 检查 bean 定义是否存在于该工厂
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // 没有找到,即不存在 -> 检查父类
      String nameToLookup = originalBeanName(name);
      if (args != null) {
        // 有更精确的参数 -> 委托给父类
        return (T) parentBeanFactory.getBean(nameToLookup, args);
      }
      else {
        // 无参数 -> 委托给标准的 getBean 方法
        return parentBeanFactory.getBean(nameToLookup, requiredType);
      }
    }
    if (!typeCheckOnly) {
      markBeanAsCreated(beanName);
    }
    try {
               //根据 Bean 名取得 BeanDefinition  
      final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);
               //递归获得当前 Bean 依赖的所有Bean,确保它们的初始化
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
          }
          registerDependentBean(dep, beanName);
          getBean(dep);
        }
      }
               // 创建 singleton bean 的地方
      if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
          @Override
          public Object getObject() throws BeansException {
            try {
                               // 调用 createBean 创建单例 bean实例
              return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
              // Explicitly remove instance from singleton cache: It might have been put there
              // eagerly by the creation process, to allow for circular reference resolution.
              // Also remove any beans that received a temporary reference to the bean.
              destroySingleton(beanName);
              throw ex;
            }
          }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
               // 创建 prototype bean 的地方
      else if (mbd.isPrototype()) {
        // It's a prototype -> create a ne w instance.
        Object prototypeInstance = null;
        try {
          beforePrototypeCreation(beanName);
          prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
          afterPrototypeCreation(beanName);
        }
        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }
      else {
        String scopeName = mbd.getScope();
        final Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
          Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
              beforePrototypeCreation(beanName);
              try {
                return createBean(beanName, mbd, args);
              }
              finally {
                afterPrototypeCreation(beanName);
              }
            }
          });
          bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
        }
        catch (IllegalStateException ex) {
          throw new BeanCreationException(beanName,
              "Scope '" + scopeName + "' is not active for the current thread; consider " +
              "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
              ex);
        }
      }
    }
    catch (BeansException ex) {
      cleanupAfterBeanCreationFailure(beanName);
      throw ex;
    }
  }
  // Check if required type matches the type of the actual bean instance.
       // 这里对创建的Bean进行类型检查,如果没有问题,就返回这个新创建的Bean,这个Bean已经是包含了依赖关系的Bean
  if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
    try {
      return getTypeConverter().convertIfNecessary(bean, requiredType);
    }
    catch (TypeMismatchException ex) {
      if (logger.isDebugEnabled()) {
        logger.debug("Failed to convert bean '" + name + "' to required type '" +
            ClassUtils.getQualifiedName(requiredType) + "'", ex);
      }
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    }
  }
  return (T) bean;
}

依赖注入的发生是在容器中的 BeanDefinition 数据已经建立好的前提下进行的.

目录
相关文章
|
10天前
|
Java 测试技术 开发者
IoC容器有什么作用?
【4月更文挑战第30天】IoC容器有什么作用?
29 0
|
10天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
10天前
|
Java 开发者 容器
IoC容器如何实现依赖注入?
【4月更文挑战第30天】IoC容器如何实现依赖注入?
20 0
|
10天前
|
XML Java 数据格式
如何配置IoC容器?
【4月更文挑战第30天】如何配置IoC容器?
18 0
|
10天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
19 0
|
17小时前
|
NoSQL Redis Docker
Mac上轻松几步搞定Docker与Redis安装:从下载安装到容器运行实测全程指南
Mac上轻松几步搞定Docker与Redis安装:从下载安装到容器运行实测全程指南
7 0
|
2天前
|
监控 Kubernetes Docker
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
【5月更文挑战第9天】本文探讨了Docker容器中应用的健康检查与自动恢复,强调其对应用稳定性和系统性能的重要性。健康检查包括进程、端口和应用特定检查,而自动恢复则涉及重启容器和重新部署。Docker原生及第三方工具(如Kubernetes)提供了相关功能。配置检查需考虑检查频率、应用特性和监控告警。案例分析展示了实际操作,未来发展趋势将趋向更智能和高效的检查恢复机制。
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
|
2天前
|
存储 安全 数据库
【Docker 专栏】Docker 容器内应用的状态持久化
【5月更文挑战第9天】本文探讨了Docker容器中应用状态持久化的重要性,包括数据保护、应用可用性和历史记录保存。主要持久化方法有数据卷、绑定挂载和外部存储服务。数据卷是推荐手段,可通过`docker volume create`命令创建并挂载。绑定挂载需注意权限和路径一致性。利用外部存储如数据库和云服务可应对复杂需求。最佳实践包括规划存储策略、定期备份和测试验证。随着技术发展,未来将有更智能的持久化解决方案。
【Docker 专栏】Docker 容器内应用的状态持久化
|
2天前
|
机器学习/深度学习 监控 Kubernetes
【Docker 专栏】Docker 容器内服务的自动扩展与缩容
【5月更文挑战第9天】本文探讨了Docker容器服务的自动扩展与缩容原理及实践,强调其在动态业务环境中的重要性。通过选择监控指标(如CPU使用率)、设定触发条件和制定扩展策略,实现资源的动态调整。方法包括云平台集成和使用Kubernetes等框架。实践中,电商平台和实时数据处理系统受益于此技术。注意点涉及监控数据准确性、扩展速度和资源分配。未来,智能算法将提升扩展缩容的效率和准确性,成为关键技术支持。
【Docker 专栏】Docker 容器内服务的自动扩展与缩容
|
2天前
|
Java 数据库连接 Docker
【Docker 专栏】Docker 容器内环境变量的管理与使用
【5月更文挑战第9天】本文介绍了Docker容器中环境变量的管理与使用,环境变量用于传递配置信息和设置应用运行环境。设置方法包括在Dockerfile中使用`ENV`指令或在启动容器时通过`-e`参数设定。应用可直接访问环境变量或在脚本中使用。环境变量作用包括传递配置、设置运行环境和动态调整应用行为。使用时注意变量名称和值的合法性、保密性和覆盖问题。理解并熟练运用环境变量能提升Docker技术的使用效率和软件部署质量。
【Docker 专栏】Docker 容器内环境变量的管理与使用