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

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()。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342