写点什么

Spring 源码解析 (五)Spring 加载 bean 依赖注入

  • 2022 年 8 月 31 日
    江西
  • 本文字数:4296 字

    阅读完需:约 14 分钟

Spring源码解析(五)Spring 加载bean 依赖注入

作者石臻臻,CSDN 博客之星 Top5Kafka Contributornacos Contributor华为云 MVP,腾讯云 TVP,滴滴 Kafka 技术专家 KnowStreaming, 《Kafka 运维与实战宝典》电子书作者。领取《Kafka 运维与实战宝典》PDF 请联系石臻臻


KnowStreaming 是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源!


在开始这篇文章之前,我们简单回顾一下前面几篇文章的内容

至此,我们分析过后,可以简单的归纳一下 Spring IOC 容器的过程一、Resource 定位过程 这个 Resource 定位指的的是 BeanDefinition 的资源定位,他由 ResourceLoader 通过统一的 Resource 接口来完成,这个 Resource 对各种形式的 BeanDefinition 的使用都提供了统一接口,对于这些 BeanDefinition 的存在形式,相信大家都不会感到陌生,比如,在类路径中的 Bean 定义信息可以使用 FileSystemResouce 来进行抽象;在类路径中的 Bean 定义信息可以使用前面提到的 ClassPathResource 来使用,等待;这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样;

二、BeanDefinition 的载入,这个载入过程是把用户定义好的 Bean 表示成 IOC 容易内部的数据结构,而这个容器内部的数据结构就是 BeanDefinition,这个 BeanDefinition 实际上就是 POJO 对象在 IOC 容器中的抽象,通过这个 BeanDefinition 定义的数据结构,使 IOC 容器能够方便的对 POJO 对象也就是 bean 进行管理,

三、向 IOC 容器中注册这些 BeanDefinition 的过程。这个过程通过调用 BeanDefinitionRegistery 接口的实现来完成的;例如 DefaultListableBeanFactory 就实现了 BeanDefinitionRegistry 接口,这个注册过程把载入的 BeanDefinition 向 Ioc 容器中注册;可以看到,BeanDefinition 是载入到了一个 ConcurrentHashMap 中;

四、IOC 容器的依赖注入; 这个过程一般发生在应用第一次通过 getBean 方法向容器中索取 bean 的时候,但是有一个例外需要注意,在 Ioc 容器中有一个预实例化的配置,也就是 lazy-init ;用户可以对容器的初始化过程做一个微小的控制,从而改变这个设置了 lazy-init 属性的 bean 的依赖注入过程。Lazy-init是怎么控制加载的

那么今天这篇文章我们主要来分析一下,IOC 容器是如何将我们的内部数据 BeanDefinition 加载成我们正在在程序中需要的实例对象的;

1AbstractBeanFactory

代码分析入口:

 public static void main(String[] args){        ClassPathResource resource = new ClassPathResource("SpringContextConfig.xml");        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);        //解析和注册阶段         reader.loadBeanDefinitions(resource);        //这里是加载阶段        BeanA beanA = (BeanA)factory.getBean("beanA");        System.out.print("beanA");    }
复制代码

我们用了最简单的 BeanFactory 来加载并且分析,像那些更高级的例如 WebApplicationContext 等容器我们回头再来分析。加载阶段是在用户第一次调用的时候才会去加载;show the code:

 /** 存放所有至少创建过一次 的所有beanName */ private final Set<String> alreadyCreated =   Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(256)); /** Map from bean name to merged RootBeanDefinition */ private final Map<String, RootBeanDefinition> mergedBeanDefinitions =   new ConcurrentHashMap<String, RootBeanDefinition>(256);   protected <T> T doGetBean(   final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)   throws BeansException {  /**  * 1.转换一下beanName,主要是如果有  & 开头的给去掉  * 2.如果name是别名的话,则this.aliasMap.get(canonicalName)取出beanName  */  final String beanName = transformedBeanName(name);  Object bean;
  //根据beanName得到单例实例化对象,检查单例对象是否实例化并且  // 在创建单例对象的时候允许提前引用(这个主要是用于解决循环引用;这个回头单独分析TODO...)  /**  *根据beanName得到单例实例化对象,检查单例对象是否实例化并且  * 在创建单例对象的时候允许提前引用  * spring解决循环引用就是利用三级缓存实现的;  * 这里会提前返回引用对象的  */  Object sharedInstance = getSingleton(beanName);
复制代码

跳转到这个方法去Object sharedInstance = getSingleton(beanName);

    if (sharedInstance != null && args == null) {   if (logger.isDebugEnabled()) {    if (isSingletonCurrentlyInCreation(beanName)) {     logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +       "' that is not fully initialized yet - a consequence of a circular reference");    }    else {     logger.debug("Returning cached instance of singleton bean '" + beanName + "'");    }   }   /**   *处理FactoryBean类型的bean;   *1.不是FactoryBean或者是FactoryBean但是name有前缀& 直接返回sharedInstanc;   *2.如果是FactoryBean并且name没有前缀& 则返回FactoryBean.getObject()方法生成的对象;   *  2.1 如果是2的情况,还有执行一下所有BeanPostProcessors类型后置处理器的postProcessAfterInitialization()方法;注意:只执行了这个方法,没有执行postProcessBeforeInitialization方法;   * 3.关于 BeanPostProcessor 详见 博文https://blog.csdn.net/u010634066/article/details/80289441    */   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  }
  else {//如果是第一次创建,或者args!=null ,或者不是单例对象   //如果我们创建对象失败了,我们很可能在循环引用里面了   //TODO ....   if (isPrototypeCurrentlyInCreation(beanName)) {    throw new BeanCurrentlyInCreationException(beanName);   }
   // 获取父容器 TODO....父子容器可以单独开一篇文章来讲解了;   BeanFactory parentBeanFactory = getParentBeanFactory();   //如果父容器存在,并且beanName在当前的容器不存在,那就去父容器查找   if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {    // Not found -> check parent.    /**    *1.判断beanName 是否存在 & ;如果有的话去掉    *2.检查是不是别名,如果是别名改成正真的beanName;    *3.判断如果传进来的带有  &; 则给它重新加上    */    String nameToLookup = originalBeanName(name);    if (args != null) {     // 委托父容器调用getBean操作 有参数;     return (T) parentBeanFactory.getBean(nameToLookup, args);    }    else {     // 去父容器调用getBean操作;无参数     return parentBeanFactory.getBean(nameToLookup, requiredType);    }   }      //这个???   if (!typeCheckOnly) {    //标记这个bean被创建过 this.alreadyCreated.add(beanName);    markBeanAsCreated(beanName);   }
   try {   /*父子BeanDe合并;当给定的bean 是一个childe类型,它还有parent属性;则将parent的属性和childe属性合并一下;*/    //看下文详细代码    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);    //检查mbd是不是 设置了属性 abstract=true;类似于抽象类,不能去实例化;抛出异常    checkMergedBeanDefinition(mbd, beanName, args);
    // Guarantee initialization of beans that the current bean depends on.    /**是否有依赖的bean;例如:<bean id = "hw"  class="src.HelloWorld" lazy-init="true"  depends-on="beanA"> ;那么hw必须要在beanA之后实例化*/    String[] dependsOn = mbd.getDependsOn();    //注意这里只是  deponse-on 属性的依赖;跟bean内部依赖另外的bean不是一回事    if (dependsOn != null) {     for (String dep : dependsOn) {
复制代码

跳转到isDependent

     /**     *1.检查是否有循环依赖;例如:<bean id = "hw"  class="src.HelloWorld" lazy-init="true"  depends-on="beanA">这个hw依赖了 beanA 然后beanA又依赖了hw;<bean id="beanA" class="src.lookupmethods.BeanA" depends-on="hw">这样就会抛出异常     */      if (isDependent(beanName, dep)) {       throw new BeanCreationException(mbd.getResourceDescription(), beanName,         "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");      }      /**      *例子:<bean id = "hw"  class="src.HelloWorld" lazy-init="true"  depends-on="beanA">      *1.标记依赖bean;这里面做了两件事情;      *2.第一件是 this.dependentBeanMap.put(canonicalName, dependentBeans);这个dependentBeanMap表示的是 某个bean被哪些bean给依赖了;在上面的配置中;dependentBeanMap存了一个 key为beanA;value为 (Set<String>中有个hw;存了所有需要依赖beanA的beanName)      *3.第二件事是this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);      *这个跟2.想反,这个存的是hw需要依赖的所有beanName      */      registerDependentBean(dep, beanName);      //依赖注入!!!有依赖的情况,递归调用被依赖的beanName(这里是 去get beanA)      getBean(dep);     }    }
复制代码

跳转到getSingleton(beanName, new ObjectFactory


发布于: 刚刚阅读数: 4
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 20w字《Kafka运维与实战宝典》PDF下载请关注公众号:石臻臻的杂货铺

评论

发布
暂无评论
Spring源码解析(五)Spring 加载bean 依赖注入_spring 源码_石臻臻的杂货铺_InfoQ写作社区