二十一、创建Bean之实例化Bean对象一
createBeanInstance()
方法
创建Bean实例过程的第一步:实例化Bean,方法为createBeanInstance()
:
1 |
|
实例化 Bean 对象,主要的逻辑为:
<1>
处,如果存在 Supplier 回调,则调用obtainFromSupplier(Supplier instanceSupplier, String beanName)
方法,进行初始化。<2>
处,如果存在工厂方法,则使用工厂方法进行初始化。<3>
处,首先判断缓存,如果缓存中存在,即已经解析过了,则直接使用已经解析了的。根据constructorArgumentsResolved
参数来判断:<3.1>
处,是使用构造函数自动注入,即调用autowireConstructor(String beanName, RootBeanDefinition mbd, Constructor[] ctors, Object[] explicitArgs)
方法。<3.2>
处,还是默认构造函数,即调用instantiateBean(final String beanName, final RootBeanDefinition mbd)
方法。<4>
处,如果缓存中没有,则需要先确定到底使用哪个构造函数来完成解析工作,因为一个类有多个构造函数,每个构造函数都有不同的构造参数,所以需要根据参数来锁定构造函数并完成初始化。<4.1>
处,如果存在参数,则使用相应的带有参数的构造函数,即调用autowireConstructor(String beanName, RootBeanDefinition mbd, Constructor[] ctors, Object[] explicitArgs)
方法。<4.2>
处,否则,使用默认构造函数,即调用instantiateBean(final String beanName, final RootBeanDefinition mbd)
方法。
obtainFromSupplier()
方法
如果存在 Supplier 回调,则使用给定的回调方法初始化策略。
1 |
|
首先,从 BeanDefinition 中获取 Supplier 对象。如果不为空,则调用 obtainFromSupplier(final String beanName, final RootBeanDefinition mbd)
方法。
Supplier
Supplier
:java1.8的一个函数式接口,提供者。用于指定创建 bean 的回调。如果我们设置了这样的回调,那么其他的构造器或者工厂方法都会没有用。
1 |
|
如何设置Supplier
参数,有对应的setter
方法:
1 |
|
在构造 BeanDefinition 对象的时候,设置了 instanceSupplier
该值,(以 RootBeanDefinition 为例):
1 |
|
obtainFromSupplier()
方法
如果设置了 instanceSupplier
属性,则可以调用 obtainFromSupplier(Supplier instanceSupplier, String beanName)
方法,完成 Bean 的初始化:
1 |
|
流程如下:
<1>
首先,调用 Supplier 的get()
方法,获得一个 Bean 实例对象。<2>
然后,根据该实例对象构造一个 BeanWrapper 对象bw
。<3>
最后,初始化该对象。
instantiateUsingFactoryMethod()
方法
如果存在工厂方法,使用 FactoryBean 的 factory-method 来创建,支持静态工厂和实例工厂:
1 |
|
ConstructorResolver
org.springframework.expression.ConstructorResolver
是构造方法或者工厂类初始化 bean 的委托类。
1 |
|
以上该方法:确定工厂对象,然后获取构造函数和构造参数,最后调用 InstantiationStrategy 对象的 instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner, Constructor ctor, Object... args)
方法,来创建 Bean 实例。
一、<1>确定工厂对象
首先获取工厂方法名:
- 若工厂方法名不为空,则调用
AbstractAutowireCapableBeanFactory.getBean(String name)
方法,获取工厂对象, - 若为空,则可能为一个静态工厂,对于静态工厂则必须提供工厂类的全类名,同时设置
factoryBean = null
。
- 若工厂方法名不为空,则调用
二、<2>构造参数确认
工厂对象确定后,则是确认构造参数。构造参数的确认主要分为三种情况:
explicitArgs
参数缓存中获取
配置文件中解析
<2.1>explicitArgs 参数
explicitArgs
参数,是我们调用getBean(...)
方法时传递进来的。一般该参数,该参数就是用于初始化 Bean 时所传递的参数。如果该参数不为空,则可以确定构造函数的参数就是它了。<2.2>缓存中获取
在该方法的最后,我们会发现这样一段
argsHolderToUse.storeCache(mbd, factoryMethodToUse)
代码。这段代码主要是将构造函数、构造参数保存到缓存中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// ConstructorResolver.ArgumentsHolder.java
public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;
mbd.constructorArgumentsResolved = true;
if (this.resolveNecessary) {
mbd.preparedConstructorArguments = this.preparedArguments;
} else {
mbd.resolvedConstructorArguments = this.arguments;
}
}
}
// RootBeanDefinition.java
// 构造函数的缓存锁
final Object constructorArgumentLock = new Object();
// 缓存已经解析的构造函数或者工厂方法
@Nullable
Executable resolvedConstructorOrFactoryMethod;
// 标记字段,标记构造函数、参数已经解析了。默认为 `false`
boolean constructorArgumentsResolved = false;
// 缓存已经解析的构造函数参数,包可见字段。
@Nullable
Object[] resolvedConstructorArguments;从缓存中获取就是提取以上几个参数的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// ConstructorResolver.java
// 没有指定,则尝试从配置文件中解析
Object[] argsToResolve = null;
// <2.2> 首先尝试从缓存中获取
synchronized (mbd.constructorArgumentLock) {
// 获取缓存中的构造函数或者工厂方法
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached factory method...
// 获取缓存中的构造参数
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
// 获取缓存中的构造函数参数的包可见字段
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
// 缓存中存在,则解析存储在 BeanDefinition 中的参数
// 如给定方法的构造函数 A(int ,int ),则通过此方法后就会把配置文件中的("1","1")转换为 (1,1)
// 缓存中的值可能是原始值也有可能是最终值
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
}- 如果缓存中存在构造参数,则需要调用
resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, Executable executable, Object[] argsToResolve, boolean fallback)
方法,进行转换。 - 因为缓存中的值有可能是最终值,也有可能不是最终值。比如我们构造函数中的类型为 Integer 类型的 1 ,但是原始的参数类型有可能是 String 类型的
"1"
,所以即便是从缓存中得到了构造参数,也需要经过一番的类型转换确保参数类型完全对应。
<2.3>配置文件中解析
即没有通过传递参数的方式传递构造参数,缓存中也没有,那就只能通过解析配置文件获取构造参数了。在 bean 解析类的博文中我们了解了,配置文件中的信息都会转换到 BeanDefinition 实例对象中,所以配置文件中的参数可以直接通过 BeanDefinition 对象获取。
1
2
3
4
5
6
7
8
9
10
11// AbstractAutowireCapableBeanFactory.java
// <2.3> getBean() 没有传递参数,则需要解析保存在 BeanDefinition 构造函数中指定的参数
if (mbd.hasConstructorArgumentValues()) {
// <2.3.1> 构造函数的参数
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
// <2.3.2> 解析构造函数的参数
// 将该 bean 的构造函数参数解析为 resolvedValues 对象,其中会涉及到其他 bean
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}<2.3.1>
,通过 BeanDefinition 的getConstructorArgumentValues()
方法,就可以获取构造信息了。<2.3.2>
,有了构造信息就可以获取相关的参数值信息了,获取的参数信息包括直接值和引用,这一步骤的处理交由resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues)
方法来完成。该方法会将构造参数信息解析为resolvedValues
对象 并返回解析到的参数个数minNrOfArgs
。
- 如果缓存中存在构造参数,则需要调用
<3>构造函数
确定构造参数后,下一步则是确定构造函数。
- 第一步,是通过
getCandidateMethods()
方法,获取所有的构造方法,同时对构造方法进行刷选。 - 然后,在对其进行排序处理(
candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR)
)。排序的主要目的,是为了能够更加方便的找到匹配的构造函数,因为构造函数的确认是根据参数个数确认的。排序的规则是:先按照public
/ 非public
构造函数升序,再按照构造参数数量降序。
通过迭代
candidates
(包含了所有要匹配的构造函数)的方式,依次比较其参数:- 如果显示提供了参数(
explicitArgs != null
),则直接比较两者长度是否相等,如果相等则表示找到了,否则继续比较。 - 如果没有显示提供参数,则需要获取
org.springframework.core.ParameterNameDiscoverer
对象。该对象为参数名称探测器,主要用于发现方法和构造函数的参数名称。
将参数包装成
ConstructorResolver.ArgumentsHolder
对象。该对象用于保存参数,我们称之为参数持有者。当将对象包装成 ArgumentsHolder 对象后,我们就可以通过它来进行构造函数匹配。匹配分为严格模式和宽松模式:- 严格模式:解析构造函数时,必须所有参数都需要匹配,否则抛出异常。
- 宽松模式:使用具有”最接近的模式”进行匹配。
判断的依据是根据 BeanDefinition 的
isLenientConstructorResolution
属性(该参数是我们在构造 AbstractBeanDefinition 对象是传递的)来获取类型差异权重(typeDiffWeight
) 的。- 如果
typeDiffWeight < minTypeDiffWeight
,则代表“最接近的模式”,选择其作为构造函数。 - 否则,只有两者具有相同的参数数量,且类型差异权重相等才会纳入考虑范围。
- 第一步,是通过
<4>创建Bean实例
工厂对象、构造函数、构造参数都已经确认了,则最后一步就是调用
org.springframework.beans.factory.support.InstantiationStrategy
对象的.instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner, Object factoryBean, final Method factoryMethod, @Nullable Object... args)
方法,来创建 bean 实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
try {
// 设置 Method 可访问
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
ReflectionUtils.makeAccessible(factoryMethod);
}
// 获得原 Method 对象
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
// 设置新的 Method 对象,到 currentlyInvokedFactoryMethod 中
currentlyInvokedFactoryMethod.set(factoryMethod);
// <x> 创建 Bean 对象
Object result = factoryMethod.invoke(factoryBean, args);
// 未创建,则创建 NullBean 对象
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
// 设置老的 Method 对象,到 currentlyInvokedFactoryMethod 中
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
catch (IllegalArgumentException ex) {throw new BeanInstantiationException(factoryMethod,
"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(factoryMethod,
"Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
}
catch (InvocationTargetException ex) {
String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
"declaring the factory method as static for independence from its containing instance. " + msg;
}
throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
}
}核心的部分,在于
<x>
处,利用 Java 反射执行工厂方法并返回创建好的实例:1
2
3// SimpleInstantiationStrategy.java
Object result = factoryMethod.invoke(factoryBean, args);