前言
Bean的创建
前面降到,Bean的创建方式又有以下几种:
- factoryMethod :
- FactoryBean + FactoryBean的非静态FactoryMethod
- 当前beanClass + 静态FactoryMethod
- 构造方法
- 带有@Autowired注解的有参构造
- 不带有@Autowired注解的有参构造
- 无参构造
上一篇讲了 FactoryMethod 方式 是如何来创建bean的,
这一篇 则主要介绍 Spring源码中 是如何通过 构造方法 来创建bean的实例的。
源码还是回到创建bean这里
如果不使用FactoryMethod,就走构造器 创建bean
1. 缓存
在判断是否使用FactoryMethod方式的代码下方,紧接着的就是 判断 该类型的bean 是否实例化过, 之前实例化 是否缓存了 构造方法, 和是否使用了构造方法的参数注入。
相关数据的缓存是存在beanDefinition里的 :
- resolvedConstructorOrFactoryMethod : 使用过的构造方法 或者 FactoryMethod。
- constructorArgumentsResolved : 构造方法 是否 使用了参数注入
- resolvedConstructorArguments : 一般如果constructorArgumentsResolved = true, 使用了参数注入,beanDefinition的resolvedConstructorArguments 将会缓存 参数列表 获取到 的 具体值。
存在缓存
如果这个bean 被处理过
- 使用了参数注入, 用有参的构造方法 来 创建bean。会从beanDefinition里取出 缓存的构造方法和 参数列表 直接反射这个方法,创建对象。
- 没有使用参数注入, 走无参构造方法。 反射无参构造方法。
记住这两个方法 :
- autowireConstructor(beanName, mbd, null, null) : 用有参的 构造方法 创建对象。
- instantiateBean(beanName, mbd) :用无参构造方法 创建对象。
作用
单例bean的话, 是用不到这个缓存的,因为对象实际只创建一次, 就是只走一次 不存在缓存,自己选择构造器 创建对象 的逻辑。第二次获取对象 从单例池里拿,直接返回, 不会再走到 创建对象的代码了。
多例Bean的话, 会使用到。每次都创建新对象, 而且多个对象 共用的是同 一个 beanDefinition ,会命中存在beanDefinition里的缓存。
2. 选择构造器
单例Bean 第一次创建对象, 肯定是 不存在缓存的,需要自己选择合适的构造器。
这里又是BeanPostProcessor的运用, AutowiredAnnotationBeanPostProcessor 类 的实例 会再这里, 进行构造器的 选择。
点进AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(Class> beanClass, final String beanName):
方法一开始是处理 @Lookup注解的, 略过。
先剩下的总体代码 :
2.1 寻找 @Autowired 注解 的构造器
- 遍历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, 走无参构造。
3. 根据返回的构造器 进行实例化 :
3.1. 没找到合适的构造器:
如果选择构造器中,没有找到构造器, 这里返回null, 直接走无参构造
最终获取无参构造, 反射出实例,返回。
3.2. 找到了 构造器列表
如果找到了构造器列表,走有参构造 实例化
3.2.1 判断是否要从缓存里取构造器和参数列表
在之前命中缓存的时候, 这个explicitArgs参数传的就是null,这个时候就从BeanDefinition的缓存里取构造器和参数列表
如果缓存里有,那么constructorToUse !=null, argsToUse != null, 下面很大的判断就不会进,直接 跳到 方法最后面, 利用方法和参数 直接反射。
反射
3.2.2 不走缓存, 筛选构造器
3.2.2.1 是否只找到一个无参构造
没走缓存, 就会进这个判断
如果只找到一个构造器,并且是无参构造, 直接实例化
3.2.2.2 按照参数列表排序
3.2.2.3 按照顺序遍历构造方法,找到一个所有传入参数都能从容器中注入的构造方法
由于构造方法的列表已经按照参数列表的长度排过序了, 长度越长的越先遍历 , 如果当前遍历的 构造方法,的所有参数都能从beanFactory中注入进来,那么
最终用来 实例化对象的构造方法和列表就是用的 当前遍历的这个。
参数注入
其中参数注入,如果参数是引用类型会 触发 getBean()操作。
createArgumentArray()方法下半部分 :
String类型的参数会从配置文件里 解析 @Value值
引用类型参数,触发getBean():
最終调用的就是 beanFactory.getBean(beanName)
3.2.2.4 缓存构造方法和参数列表
构造方法和获取到的参数列表找到之后,实例化之前,缓存一下。
3.2.2.5 反射构造方法和获取到的参数列表
回到ConstructorResolver.autowireConstructor()
4. 总结
- 带有@Autowired注解的有参构造,参数getBean
- 存在且只有一个required = true,不存在其他带有@Autowired注解的构造方法,就执行这个
如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException - 没有required = true,却存在多个required = false
会选择参数最长,且所有参数都能成功注入的构造方法
如果参数都不能注入,就会报错NoSuchBeanDefinitionException
- 存在且只有一个required = true,不存在其他带有@Autowired注解的构造方法,就执行这个
- 不带有@Autowired注解的有参构造
- 存在多个,会调用无参构造
- 仅存在一个,就用这个,参数getBean
如果参数不能都成功注入,就会报错NoSuchBeanDefinitionException
- 无参构造
直接反射
最后,被选中的构造方法 参数 如果是 字符串会从 配置文件里取(类似@Value), 如果是 引用类型 ,就是 BeanFactory.getBean()。