![](https://img.haomeiwen.com/i23353704/4c1e386beb240f2d.png)
前言
Bean的创建
前面降到,Bean的创建方式又有以下几种:
- factoryMethod :
- FactoryBean + FactoryBean的非静态FactoryMethod
- 当前beanClass + 静态FactoryMethod
- 构造方法
- 带有@Autowired注解的有参构造
- 不带有@Autowired注解的有参构造
- 无参构造
上一篇讲了 FactoryMethod 方式 是如何来创建bean的,
这一篇 则主要介绍 Spring源码中 是如何通过 构造方法 来创建bean的实例的。
源码还是回到创建bean这里
![](https://img.haomeiwen.com/i23353704/11a3518dd60e355c.png)
如果不使用FactoryMethod,就走构造器 创建bean
![](https://img.haomeiwen.com/i23353704/ebb7734ae7e1c171.png)
1. 缓存
在判断是否使用FactoryMethod方式的代码下方,紧接着的就是 判断 该类型的bean 是否实例化过, 之前实例化 是否缓存了 构造方法, 和是否使用了构造方法的参数注入。
相关数据的缓存是存在beanDefinition里的 :
- resolvedConstructorOrFactoryMethod : 使用过的构造方法 或者 FactoryMethod。
- constructorArgumentsResolved : 构造方法 是否 使用了参数注入
- resolvedConstructorArguments : 一般如果constructorArgumentsResolved = true, 使用了参数注入,beanDefinition的resolvedConstructorArguments 将会缓存 参数列表 获取到 的 具体值。
![](https://img.haomeiwen.com/i23353704/2a878e59b23d9f25.png)
存在缓存
如果这个bean 被处理过
- 使用了参数注入, 用有参的构造方法 来 创建bean。会从beanDefinition里取出 缓存的构造方法和 参数列表 直接反射这个方法,创建对象。
- 没有使用参数注入, 走无参构造方法。 反射无参构造方法。
记住这两个方法 :
- autowireConstructor(beanName, mbd, null, null) : 用有参的 构造方法 创建对象。
- instantiateBean(beanName, mbd) :用无参构造方法 创建对象。
![](https://img.haomeiwen.com/i23353704/049fed3ed043519b.png)
作用
单例bean的话, 是用不到这个缓存的,因为对象实际只创建一次, 就是只走一次 不存在缓存,自己选择构造器 创建对象 的逻辑。第二次获取对象 从单例池里拿,直接返回, 不会再走到 创建对象的代码了。
多例Bean的话, 会使用到。每次都创建新对象, 而且多个对象 共用的是同 一个 beanDefinition ,会命中存在beanDefinition里的缓存。
2. 选择构造器
单例Bean 第一次创建对象, 肯定是 不存在缓存的,需要自己选择合适的构造器。
![](https://img.haomeiwen.com/i23353704/b4a06cbea90d9a15.png)
这里又是BeanPostProcessor的运用, AutowiredAnnotationBeanPostProcessor 类 的实例 会再这里, 进行构造器的 选择。
![](https://img.haomeiwen.com/i23353704/de50588900775009.png)
点进AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(Class<?> beanClass, final String beanName):
方法一开始是处理 @Lookup注解的, 略过。
![](https://img.haomeiwen.com/i23353704/23efad774696b5fc.png)
先剩下的总体代码 :
![](https://img.haomeiwen.com/i23353704/4b75c440601c4ff0.png)
2.1 寻找 @Autowired 注解 的构造器
![](https://img.haomeiwen.com/i23353704/91d9a91c08de4269.png)
- 遍历class的所有构造方法
- 判断当前遍历的构造方法是否存在@Autowired注解
- 存在,判断是否找到 @Autowired(required = true)
- 已经找到,报错
- 没有找到, 判断自己required属性是否为true
- 是为true, 判断候选的构造方法集合中是否有元素
- 已经找到,报错。
- 没找到,将设置设置为 局部变量requiredConstructor,用来标识已经找到@Autowired(required = true) 的构造方法, 加到 候选的构造方法集合中。
- 为false,加到 候选的构造方法集合中。
- 是为true, 判断候选的构造方法集合中是否有元素
- 存在,判断是否找到 @Autowired(required = true)
总结:
其实就是 不允许 存在 同时存在 @Autowired(required = true) 和 @Autowired(required = false)的构造方法, 可以允许同时存在 多个 @Autowired(required = false) 的 构造方法。
2.2 判断寻找 @Autowired 注解 的构造器的结果
- 要么找到了@Autowired修饰的构造方法,返回
- 没找到@Autowired修饰的构造方法,看是不是 只存在 一个 有参构造方法 , 有的话就返回它
- 否则,就是返回null, 走无参构造。
![](https://img.haomeiwen.com/i23353704/2ac2a3d3dcfae787.png)
3. 根据返回的构造器 进行实例化 :
3.1. 没找到合适的构造器:
如果选择构造器中,没有找到构造器, 这里返回null, 直接走无参构造
![](https://img.haomeiwen.com/i23353704/d321ca1dd34ef035.png)
![](https://img.haomeiwen.com/i23353704/b49d6a123903c73e.png)
最终获取无参构造, 反射出实例,返回。
![](https://img.haomeiwen.com/i23353704/c282c609862a69a2.png)
3.2. 找到了 构造器列表
如果找到了构造器列表,走有参构造 实例化
![](https://img.haomeiwen.com/i23353704/80d6fc45f4a28209.png)
3.2.1 判断是否要从缓存里取构造器和参数列表
在之前命中缓存的时候, 这个explicitArgs参数传的就是null,这个时候就从BeanDefinition的缓存里取构造器和参数列表
![](https://img.haomeiwen.com/i23353704/37bfdd0deaa522a5.png)
如果缓存里有,那么constructorToUse !=null, argsToUse != null, 下面很大的判断就不会进,直接 跳到 方法最后面, 利用方法和参数 直接反射。
![](https://img.haomeiwen.com/i23353704/824f052ecb61cc1a.png)
![](https://img.haomeiwen.com/i23353704/37c3a87f5cbfb27f.png)
反射
![](https://img.haomeiwen.com/i23353704/76875937d923be89.png)
3.2.2 不走缓存, 筛选构造器
3.2.2.1 是否只找到一个无参构造
没走缓存, 就会进这个判断
![](https://img.haomeiwen.com/i23353704/ee154004be6bc842.png)
如果只找到一个构造器,并且是无参构造, 直接实例化
![](https://img.haomeiwen.com/i23353704/c08079c12c7aaf3d.png)
3.2.2.2 按照参数列表排序
![](https://img.haomeiwen.com/i23353704/2a4f856f88973669.png)
3.2.2.3 按照顺序遍历构造方法,找到一个所有传入参数都能从容器中注入的构造方法
由于构造方法的列表已经按照参数列表的长度排过序了, 长度越长的越先遍历 , 如果当前遍历的 构造方法,的所有参数都能从beanFactory中注入进来,那么
最终用来 实例化对象的构造方法和列表就是用的 当前遍历的这个。
![](https://img.haomeiwen.com/i23353704/75f4d17a22a30407.png)
参数注入
其中参数注入,如果参数是引用类型会 触发 getBean()操作。
![](https://img.haomeiwen.com/i23353704/0caab3346bffe9d8.png)
createArgumentArray()方法下半部分 :
![](https://img.haomeiwen.com/i23353704/ce380ebe47412e39.png)
![](https://img.haomeiwen.com/i23353704/78823a93b2528e89.png)
![](https://img.haomeiwen.com/i23353704/579c050c2007323d.png)
String类型的参数会从配置文件里 解析 @Value值
![](https://img.haomeiwen.com/i23353704/2da73079e14836d7.png)
引用类型参数,触发getBean():
![](https://img.haomeiwen.com/i23353704/37db2d3785f09db4.png)
![](https://img.haomeiwen.com/i23353704/398d0d6c60f5b94b.png)
![](https://img.haomeiwen.com/i23353704/567b9a82f2281458.png)
最終调用的就是 beanFactory.getBean(beanName)
![](https://img.haomeiwen.com/i23353704/563d03881dd5e0fc.png)
3.2.2.4 缓存构造方法和参数列表
构造方法和获取到的参数列表找到之后,实例化之前,缓存一下。
![](https://img.haomeiwen.com/i23353704/34193b5449ff244f.png)
3.2.2.5 反射构造方法和获取到的参数列表
回到ConstructorResolver.autowireConstructor()
![](https://img.haomeiwen.com/i23353704/e7ef5c7b8a7547e5.png)
4. 总结
- 带有@Autowired注解的有参构造,参数getBean
- 存在且只有一个required = true,不存在其他带有@Autowired注解的构造方法,就执行这个
如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException - 没有required = true,却存在多个required = false
会选择参数最长,且所有参数都能成功注入的构造方法
如果参数都不能注入,就会报错NoSuchBeanDefinitionException
- 存在且只有一个required = true,不存在其他带有@Autowired注解的构造方法,就执行这个
- 不带有@Autowired注解的有参构造
- 存在多个,会调用无参构造
- 仅存在一个,就用这个,参数getBean
如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException
- 无参构造
直接反射
最后,被选中的构造方法 参数 如果是 字符串会从 配置文件里取(类似@Value), 如果是 引用类型 ,就是 BeanFactory.getBean()。
网友评论