美文网首页面试SpringBoot收藏
【Spring源码】9.IOC之创建bean对象之构造方法

【Spring源码】9.IOC之创建bean对象之构造方法

作者: 天还下着毛毛雨 | 来源:发表于2022-01-24 20:42 被阅读0次
    image

    前言

    Bean的创建

    前面降到,Bean的创建方式又有以下几种:

    1. factoryMethod :
      1. FactoryBean + FactoryBean的非静态FactoryMethod
      2. 当前beanClass + 静态FactoryMethod
    2. 构造方法
      1. 带有@Autowired注解的有参构造
      2. 不带有@Autowired注解的有参构造
      3. 无参构造

    上一篇讲了 FactoryMethod 方式 是如何来创建bean的,

    这一篇 则主要介绍 Spring源码中 是如何通过 构造方法 来创建bean的实例的。

    源码还是回到创建bean这里

    image

    如果不使用FactoryMethod,就走构造器 创建bean

    image

    1. 缓存

    在判断是否使用FactoryMethod方式的代码下方,紧接着的就是 判断 该类型的bean 是否实例化过, 之前实例化 是否缓存了 构造方法, 和是否使用了构造方法的参数注入。

    相关数据的缓存是存在beanDefinition里的 :

    1. resolvedConstructorOrFactoryMethod : 使用过的构造方法 或者 FactoryMethod。
    2. constructorArgumentsResolved : 构造方法 是否 使用了参数注入
    3. resolvedConstructorArguments : 一般如果constructorArgumentsResolved = true, 使用了参数注入,beanDefinition的resolvedConstructorArguments 将会缓存 参数列表 获取到 的 具体值。
    image

    存在缓存

    如果这个bean 被处理过

    1. 使用了参数注入, 用有参的构造方法 来 创建bean。会从beanDefinition里取出 缓存的构造方法和 参数列表 直接反射这个方法,创建对象。
    2. 没有使用参数注入, 走无参构造方法。 反射无参构造方法。

    记住这两个方法 :

    1. autowireConstructor(beanName, mbd, null, null) : 用有参的 构造方法 创建对象。
    2. instantiateBean(beanName, mbd) :用无参构造方法 创建对象。
    image

    作用

    单例bean的话, 是用不到这个缓存的,因为对象实际只创建一次, 就是只走一次 不存在缓存,自己选择构造器 创建对象 的逻辑。第二次获取对象 从单例池里拿,直接返回, 不会再走到 创建对象的代码了。

    多例Bean的话, 会使用到。每次都创建新对象, 而且多个对象 共用的是同 一个 beanDefinition ,会命中存在beanDefinition里的缓存。

    2. 选择构造器

    单例Bean 第一次创建对象, 肯定是 不存在缓存的,需要自己选择合适的构造器。

    image

    这里又是BeanPostProcessor的运用, AutowiredAnnotationBeanPostProcessor 类 的实例 会再这里, 进行构造器的 选择。

    image

    点进AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(Class<?> beanClass, final String beanName):

    方法一开始是处理 @Lookup注解的, 略过。

    image

    先剩下的总体代码 :

    image

    2.1 寻找 @Autowired 注解 的构造器

    image
    1. 遍历class的所有构造方法
    2. 判断当前遍历的构造方法是否存在@Autowired注解
      1. 存在,判断是否找到 @Autowired(required = true)
        1. 已经找到,报错
        2. 没有找到, 判断自己required属性是否为true
          1. 是为true, 判断候选的构造方法集合中是否有元素
            1. 已经找到,报错。
            2. 没找到,将设置设置为 局部变量requiredConstructor,用来标识已经找到@Autowired(required = true) 的构造方法, 加到 候选的构造方法集合中。
          2. 为false,加到 候选的构造方法集合中

    总结:

    其实就是 不允许 存在 同时存在 @Autowired(required = true) 和 @Autowired(required = false)的构造方法, 可以允许同时存在 多个 @Autowired(required = false) 的 构造方法。

    2.2 判断寻找 @Autowired 注解 的构造器的结果

    1. 要么找到了@Autowired修饰的构造方法,返回
    2. 没找到@Autowired修饰的构造方法,看是不是 只存在 一个 有参构造方法 , 有的话就返回它
    3. 否则,就是返回null, 走无参构造。
    image

    3. 根据返回的构造器 进行实例化 :

    3.1. 没找到合适的构造器:

    如果选择构造器中,没有找到构造器, 这里返回null, 直接走无参构造

    image image

    最终获取无参构造, 反射出实例,返回。

    image

    3.2. 找到了 构造器列表

    如果找到了构造器列表,走有参构造 实例化

    image

    3.2.1 判断是否要从缓存里取构造器和参数列表

    在之前命中缓存的时候, 这个explicitArgs参数传的就是null,这个时候就从BeanDefinition的缓存里取构造器和参数列表

    image

    如果缓存里有,那么constructorToUse !=null, argsToUse != null, 下面很大的判断就不会进,直接 跳到 方法最后面, 利用方法和参数 直接反射。

    image image

    反射

    image

    3.2.2 不走缓存, 筛选构造器

    3.2.2.1 是否只找到一个无参构造

    没走缓存, 就会进这个判断

    image

    如果只找到一个构造器,并且是无参构造, 直接实例化

    image

    3.2.2.2 按照参数列表排序

    image

    3.2.2.3 按照顺序遍历构造方法,找到一个所有传入参数都能从容器中注入的构造方法

    由于构造方法的列表已经按照参数列表的长度排过序了, 长度越长的越先遍历 , 如果当前遍历的 构造方法,的所有参数都能从beanFactory中注入进来,那么

    最终用来 实例化对象的构造方法和列表就是用的 当前遍历的这个。

    image
    参数注入

    其中参数注入,如果参数是引用类型会 触发 getBean()操作。

    image

    createArgumentArray()方法下半部分 :

    image image image
    String类型的参数会从配置文件里 解析 @Value值
    image
    引用类型参数,触发getBean():
    image image image

    最終调用的就是 beanFactory.getBean(beanName)

    image

    3.2.2.4 缓存构造方法和参数列表

    构造方法和获取到的参数列表找到之后,实例化之前,缓存一下。

    image

    3.2.2.5 反射构造方法和获取到的参数列表

    回到ConstructorResolver.autowireConstructor()

    image

    4. 总结

    1. 带有@Autowired注解的有参构造,参数getBean
      1. 存在且只有一个required = true,不存在其他带有@Autowired注解的构造方法,就执行这个
        如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException
      2. 没有required = true,却存在多个required = false
        会选择参数最长,且所有参数都能成功注入的构造方法
        如果参数都不能注入,就会报错NoSuchBeanDefinitionException
    2. 不带有@Autowired注解的有参构造
      1. 存在多个,会调用无参构造
      2. 仅存在一个,就用这个,参数getBean
        如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException
    3. 无参构造
      直接反射

    最后,被选中的构造方法 参数 如果是 字符串会从 配置文件里取(类似@Value), 如果是 引用类型 ,就是 BeanFactory.getBean()。

    相关文章

      网友评论

        本文标题:【Spring源码】9.IOC之创建bean对象之构造方法

        本文链接:https://www.haomeiwen.com/subject/srqihrtx.html