Spring IoC 依赖注入(四)构造器或工厂注入
Spring IoC 依赖注入(四)构造器或工厂注入
Spring 核心编程思想目录:https://www.cnblogs.com/binarylei/p/12290153.html
特别声明: Bean 完整创建过程,包括实例化、依赖注入、初始化阶段 。本文中 Bean 实例化指的是 Bean 对象的创建的第一阶段 - 实例化,不包括之后的属性注入、初始化。为了避免混淆,在此做一个约定,创建阶段指完整的对象创建过程,实例化阶段则单指第一阶段。Spring 构造器注入或工厂注入也是在实例化过程中完成的,至于实例化之后的依赖注入指的是其它的所有注入方式,如 Settter 注入,字段注入等。
Bean 实例化大致可以分为以下场景:
- 构造器:包括无参构造器和有参构造器。
- 工厂方法:包括无参工厂方法和有参工厂方法。也包括静态工厂和实例工厂。
本文重点分析以下方法:
-
beanFactory#doCreateBean:Bean 完整的创建过程,包括 Bean 实例化、依赖注入、初始化全部操作。
-
beanFactory#createBeanInstance:Bean 实例化过程。包括构造器、工厂方法,无参和有参,实例工厂和静态工厂这些创建方式。
-
InstantiationStrategy:Spring 实例化策略接口提供了三个方法,分别用于实例化无参构造器,有参构造器,工厂方法。默认实现是 CglibSubclassingInstantiationStrategy。
-
beanFactory#instantiateBean:无参实例化 instantiateBean 方法,本质是直接获取对象的默认构造器,通过反射创建对象,也就是调用 instantiationStrategy.instantiate 方法。
-
beanFactory#autowireConstructor:传统方式,构造器实例化。委托给 ConstructorResolver#autowireConstructor。
-
beanFactory#instantiateUsingFactoryMethod:一般表示注解驱动( @Bean 注册),工厂方法实例化。委托给 ConstructorResolver#instantiateUsingFactoryMethod。
-
ConstructorResolver#autowireConstructor:构造器实例化,主要解析构造器匹配和参数解析两个问题。外部化参数覆盖配置参数,且只能精确匹配,不推荐使用。默认配置参数允许参数个数大于配置的参数个数,不中的参数从 Spring IoC 容器中中查找。
-
ConstructorResolver 参数解析:ConstructorResolver 提供了以下方法进行参数解析:
- resolveConstructorArguments:解析默认的配置参数 mbd.constructorArgumentValues。
- createArgumentArray:除了已有的默认参数外,还会从容器中解析(仅当 autowiring=true),并将解析的结果包装成 ArgumentsHolder。
- resolvePreparedArguments:解析已部分解析的参数 mbd.preparedArguments。
1. doCreateBean
Bean 完整的创建过程,包括 Bean 实例化、属性注入、初始化全部操作。本文重点关注实例过程 createBeanInstance。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd,
final @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 1. 缓存的提前初始化的FactoryBean,为什么会提前初始化。
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 2. 实例化Bean,构造器注入或工厂方法注入
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 3. 属性注入、初始化 ...
Object exposedObject = bean;
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
}
说明: 我们重点说一下为什么会在缓存中获取到提前部分初始化的 FactoryBean?
很明显这些 factoryBean 实例还需要进行属性注入、初始化等操作。我们知道,Spring 在提取对象类型时,FactoryBean 需要调用其 getObjectType 方法,此时就需要初始化 factoryBean 实例,但 Spring 为了减少提前初始化 Bean 产生的问题,只会将 factoryBean 提前实例化后缓存到 factoryBeanInstanceCache,当真正需要使用到 factoryBean 才进行完整的创建。事实上,只要我们进行依赖注入,调用 beanFactory#isTypeMatch 匹配对象类型,就会遍历所有注册的 beanDefinitionNames,从而实例化所有的 factoryBean 到 factoryBeanInstanceCache 中。
这就是 factoryBeanInstanceCache 中 factoryBean 实例的由来,具体见方法见 beanFactory#getSingletonFactoryBeanForTypeCheck,最终其实也是调用 beanFactory#createBeanInstance 方法。
2. createBeanInstance
createBeanInstance 方法用于 Bean 对象的实例化,包括工厂方法创建和构造器创建两种。
- 校验工作。解析 bd.beanClassName 为 bd.beanClass。当然如果是注解驱动,则不会设置 bd.beanClass 属性,直接忽略。如果设置了 bd.beanClass,则需要校验该类是否允许访问。
- Supplier 创建对象:Spring 新推出的 bean 创建方式。
- 工厂创建:实际上,大部分情况都是注解驱动,因为 @Bean 本质是通过工厂方法创建的对象。全部委托给 instantiateUsingFactoryMethod 方法。
- 构造器创建:先判断能否使用缓存快速实例化对象,如果有外部化参数 args,则不能使用缓存,因为外部化参数会覆盖配置参数 bd.args,就点对工厂方法同样有效。之后根据是否有参数或构造器判断使用有参还是无参构造器实例对象。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1.1 确保此时beanClassName已经加载,当然注解驱动时不会设置beanClassName属性
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 1.2 校验beanClass允许访问
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException();
}
// 2. Supplier创建对象
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// ========= 工厂方法实例化 =========
// 3. 工厂方法实例化,包括实例化工厂和静态工厂
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// ========= 构造器实例化 =========
// 4. 快速实例化对象,所谓的快速实例化,实际上是说使用缓存
boolean resolved = false;
boolean autowireNecessary = false;
// 4.1 args: 外部化参数,只能当无外部参数时才使用缓存。不推荐使用外部化参数
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 4.2 是否使用缓存,其中autowireNecessary表示是否使用有参构造器
// 无参时肯定不会解析,为false。有参时会解析,为true
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 4.3 使用缓存,其中autowireNecessary表示是否使用有参构造器
if (resolved) {
if (autowireNecessary) {
// 4.4 有参构造器实例化
return autowireConstructor(beanName, mbd, null, null);
} else {
// 4.5 无参构造器实例化
return instantiateBean(beanName, mbd);
}
}
// 5. 到此,只能老老实实的解析,当然解析后会将解析后的构造器或参数缓存起来
// 5.1 是否指定了构造器,ibp#determineCandidateConstructors
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 5.2 构造器实例化
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 5.3 不用管,默认都是 null。
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// 5.4 无参构造器实例化
return instantiateBean(beanName, mbd);
}
说明: createBeanInstance 方法在 bd.beanClass 校验后进行对象实例化,而 Supplier 实例化比较简单,工厂方法实例化又全部委托给了 instantiateUsingFactoryMethod 方法,基本上主要的功能就是,判断如何使用构造器实例化对象。
- 判断能否使用缓存快速实例化对象。使用缓存实例化有两个条件:
- 外部化参数为 null。Spring 中获取对象时,允许使用外部化参数 args 覆盖配置参数 bd.args。此时,缓存的构造器和参数全部失效。虽然 Spring 提供了外部化参数 args,但不推荐使用 getBean(beanName, args) 。
- 缓存命中,也就是构造器和参数已经解析完成。如果无参,则不会解析参数,此时 bd.constructorArgumentsResolved=false,而有参则为 true。也就是可以使用该变量来判断是否使用有参构造器。
- 判断使用有参还是无参构造器实例化对象。使用有参构造器实例化对象有以下条件:
- 指定构造器。ibp#determineCandidateConstructors 后置处理器可以指定实例化的构造器 ctors。AutowiredAnnotationBeanPostProcessor 会指定实例化的构造器。
- 指定注入模式为构造器自动注入模式。目前这种模式只能通过 XML 方式配置。
<bean id="beanA" class="com.binarylei.spring.ioc.BeanA" autowire="constructor"/>
。 - 指定参数。包括配置参数 bd.constructorArgumentValues 或外部参数 args。
- 有参实例调用 autowireConstructor 方法。而无参实例化调用 instantiateBean 方法。无参实例化 instantiateBean 方法比较简单,我们先看一下无参实例化。
2. 实例化策略 - InstantiationStrategy
Spring 在对象实例化上进行了抽象,提供了 InstantiationStrategy 接口用于实例对象,两个实现类 SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。
Spring 默认的实例化策略是 CglibSubclassingInstantiationStrategy,该类是为了解决 <replaced-method> 或 <lookup-method> 标签,在这种情况下,必须进行字节码提升才能实例化对象。通常我们不会用到这些标签,本文只会介绍常规的实例化方法,直接调用反射实例化对象,即其父类 SimpleInstantiationStrategy。
InstantiationStrategy 提供了三个方法,分别用于实例化无参构造器,有参构造器,工厂方法。
// 1. 无参构造器实例化
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner);
throws BeansException;
// 2. 有参构造器实例化
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, Object... args) throws BeansException;
// 3. 工厂方法实例化
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Object factoryBean, Method factoryMethod, Object... args) throws BeansException;
说明: SimpleInstantiationStrategy 实现都非常简单,无非是通过 Java 反射实例化对象。
注意:无参构造器是会缓存 bd.resolvedConstructorOrFactoryMethod。
3. instantiateBean
无参实例化 instantiateBean 方法,本质是直接获取对象的默认构造器,通过反射创建对象,也就是调用 instantiationStrategy.instantiate 方法。
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
// 1. 调用无参构造器实例化对象
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
// 2. 配置bw的类型转换器,依赖注入时可能需要对注入的属性值进行类型转换
initBeanWrapper(bw);
return bw;
}
4. autowireConstructor
autowireConstructor 和 instantiateUsingFactoryMethod 方法其实没有本质的区别,都是通过参数注入。autowireConstructor 通过构造器实例化,instantiateUsingFactoryMethod 通过特定的工厂方法实例化,如果将构造方法当作一种特殊的方法,那么这两种注入方式没有本质的区别。事实上,Spring 也是将这两种注入方式统一进行处理的。
-
autowireConstructor:传统方式,通过构造器实例化。委托给 ConstructorResolver#autowireConstructor。
-
instantiateUsingFactoryMethod:一般表示注解驱动( @Bean 注册),通过工厂方法实例化。委托给 ConstructorResolver#instantiateUsingFactoryMethod。
4.1 整体说明
本文重点分析构造器注入 autowireConstructor 方法,了解构造器注入后,工厂注入基本上如出一辙。无论是构造器注入还是工厂方法注入都会面临如下问题:
- 外部化参数 explicitArgs 覆盖配置参数 bd.args。如果调用 beanFactory#getBean(beanName, args) 时,实例化对象时,只会使用外部参数。同时外部化参数只支持精确匹配,即方法参数个数和外部化参数个数必须完全一致。但我们还是推荐使用配置参数,而不推荐使用外部化参数。这一思想贯穿了整个参数匹配的过程,阅读源码代码时要首先要有这么一个印象。
- (缓存)快速实例化对象。只要执行过一次对象创建,bd 会缓存匹配的构造器和参数。注意,如果使用外部化参数,那么不会使用缓存,因为外部化参数发生变化后,匹配的构造器和参数会改变。还有一点,如果是单例,会直接从缓存中命中对象,根本不会再次调用 autowireConstructor 方法创建对象,此时外部化参数不会生效,这可能会让使用者非常困惑,为什么外部化参数就是不生效呢?
- mbd.constructorArgumentLock:同步锁
- mbd.resolvedConstructorOrFactoryMethod:缓存构造器或工厂方法。
- mbd.constructorArgumentsResolved:参数是否解析,boolean 值。
- mbd.resolvedConstructorArguments:缓存解析后的参数。
- mbd.preparedConstructorArguments:缓存参数,使用进还需要进一步解析。
- (非缓存)匹配无参构造器。外部化参数和配置参数匹配规则相同:外部化参数和配置参数都为 null,且只有一个无参的构造器。
- (非缓存)匹配有参构造器。先计算实际可用的参数,进行参数匹配。如果匹配成功,则计算每个构造器的权重,权重越小优先级越高。如果无法匹配或有多个权重相同的构造器,则抛出异常。最后缓存匹配成功的构造器和参数。外部化参数和默认配置参数匹配规则的最大不同是:
- 外部化参数匹配:实际参数全部来源于外部化参数。也是就是说,必须精确匹配外部化参数个数,匹配规则非常简单。
- 默认配置参数匹配:实际参数除了来源于默认的配置参数外,不足的参数还可以根据其参数类型和参数名称从 Spring IoC 容器中查找(仅当autowiring=true)。也是就是说,构造参数的个数可以大于实际参数个数。当然,如果 Spring IoC 容器中查找不到对应的参数,则该构造器方法肯定无法匹配。
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
// 1. 使用缓存,快速实例化对象
Constructor<?> constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
ArgumentsHolder argsToUse = mbd.resolvedConstructorArguments;
// 非缓存,进行参数匹配
if (constructorToUse == null || argsToUse == null) {
// 2. (非缓存)匹配无参构造器
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
return ...
}
// 3. (非缓存)匹配有参构造器
for (Constructor<?> candidate : candidates) {
}
}
return bw;
}
说明: 构造器实例化对象,代码比较长。我会将其拆开,大致分为缓存匹配、无参构造器匹配,有参构造器匹配三步分。
4.2 缓存匹配
只要对象实例化后,bd 就会缓存其构造方法或参数等信息用于快速实例化对象。缓存这一块比较简单,重要的弄清楚这些缓存变量的含义,以及什么时候缓存的?
if (explicitArgs != null) {
argsToUse = explicitArgs;
} else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
说明: 可以很清楚的看到外部化参数没有使用缓存,每次都需要重新解析。至于参数的解析 resolvePreparedArguments 会在下方进行分析。
4.3 无参构造器匹配
无参构造器匹配也很简单,如果没有指定构造器,则对所有的构造器进行匹配。当只有一个无参构造器,且无论是外部化参数或配置参数都为空时,则使用无参构造器。
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
说明: 无参构造器解析后,会缓存所有的变量,不会再次解析。最复杂的是有参构造器的解析。
4.4 有参构造器匹配
有参构造器匹配比较复杂,但也是有迹可循的:
- 首先,实际参数解析。对于外部化参数而言,只能使用指定的参数。而对于默认配置参数而言,除了配置参数外,还可以根据根据其参数类型和参数名称从 Spring IoC 容器中查找(仅当autowiring=true)。也就是说外部化参数必须精确匹配外部化参数个数,而默认配置参数则构造参数的个数可以大于实际参数个数,只要可以从容器中解析到参数即可。
- 其次,权值计算。权重越小优先级越高。如果无法匹配或有多个权重相同的构造器,则抛出异常。
- 最后,缓存解析的构造器和参数,返回实例化的对象。
(1)实际参数解析
对于外部化参数就不多说了,我们重点说一下绝大多数场景,默认配置参数。首先,解析已有的配置参数;其次,根据构造器参数类型和参数名称从 Spring IoC 容器中解析剩余的参数。如果能成功解析,则命中该构造器。
// autowiring表示是否允许从 Spring IoC 容器查找依赖
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;
// 1.1 外部化参数,不需要解析
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
// 1.2 默认配置参数,先解析已有的配置参数
} else {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
int parameterCount = candidate.getParameterCount();
if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
break;
}
if (parameterCount < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
Class<?>[] paramTypes = candidate.getParameterTypes();
// 1.3 默认配置参数,如果配置参数不足,从Spring IoC中继续解析参数。如果能解析则匹配成功
if (resolvedValues != null) {
try {
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
} catch (UnsatisfiedDependencyException ex) {
causes.add(ex);
continue;
}
// 1.4 外部化参数,只需要参数个数相等即可。此时resolvedValues=null
} else {
if (parameterCount != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
...
}
说明: 根据实际的参数解析匹配可用的构造器。
- 外部化参数:精确匹配参数个数。
- 默认配置参数匹配:允许构造器参数大于默认配置参数个数,只要不足的参数能从 Spring IoC容器中解析即可。其实最关键的就是两个参数解析函数:
- resolveConstructorArguments:解析已经有默认参数 bd.constructorArgumentValues。
- createArgumentArray:除了已有的默认参数外,还会从容器中解析(仅当autowiring=true),并将解析的结果包装成 ArgumentsHolder。
- 我们重点分析一下 autowiring=true 是什么场景,也就是允许从 Spring IoC 容器中查找依赖自动注入?
- 构造器自动注入模式:即 bd.autowireMode=AUTOWIRE_CONSTRUCTOR。我们知道,除了 XML 显示配置构造器自动注入外,一般情况下 bd.autowireMode=NO,也就是返回 false。
- 显示的指定构造器:即 chosenCtors!=null,还记得我们之前分析的 createBeanInstance 方法吗?通过后置处理器 ibp#determineCandidateConstructors 可以指定实例化的构造器 ctors。如果装载AutowiredAnnotationBeanPostProcessor 后,会使用构造器自动注入。
(2)权值计算
权重越小优先级越高。如果无法匹配或有多个权重相同的构造器,则抛出异常。constructorToUse 匹配
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// 权重最高的构造器
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
// 多个权重最高的构造器
} else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
说明: constructorToUse 表示权重最高的构造器,ambiguousConstructors 表示有多个权重最高的构造器。如果没有匹配的构造器(constructorToUse=null)或有多个权重相等的构造器(ambiguousConstructors!=null)都需要抛出异常。下面我们看一下权重的计算,以 getAssignabilityWeight 为例,权重值越小优先级越高。
// paramTypes 表示构造器或方法参数类型列表
public int getAssignabilityWeight(Class<?>[] paramTypes) {
// 1. arguments表示解析后的参数
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {
return Integer.MAX_VALUE;
}
}
// 2. arguments表示解析前的参数,如配置参数
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) {
return Integer.MAX_VALUE - 512;
}
}
return Integer.MAX_VALUE - 1024;
}
(3)实例化对象
// 1. 缓存解析后的构造器和参数
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, constructorToUse);
}
// 2. 调用InstantiationStrategy实例化参数
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
说明: 在构造器实例化对象开始前,第一步就是从 bd 缓存中快速实例化对象。其中无参构造器已经在匹配无参构造器时缓存,此时需要缓存有参构造器匹配的变量。
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;
}
}
}
在 ArgumentsHolder 对象中有三个变量,都表示参数:
-
arguments:已经解析后的参数,不需要再进行解析,直接可以注入。
-
preparedArguments:已部分解析后的参数,这个主要是用来解析注入的实际参数。
-
rawArguments:原始的参数,这个主要是用来计算构造器的权重。
到目前为止,ConstructorResolver#autowireConstructor 构造器实例化已经分析完毕,autowireConstructor 方法最主要的两个功能就是解析参数,匹配构造器。构造器的匹配我们已经分析过了,接下为是遗留下来的参数解析。
5. ConstructorResolver 参数解析
ConstructorResolver 提供了以下方法进行参数解析:
- resolveConstructorArguments:解析默认的配置参数 mbd.constructorArgumentValues。
- createArgumentArray:除了已有的默认参数外,还会从容器中解析(仅当 autowiring=true),并将解析的结果包装成 ArgumentsHolder。
- resolvePreparedArguments:解析已部分解析的参数 mbd.preparedArguments。
5.1 resolveConstructorArguments
resolveConstructorArguments 方法解析默认的配置参数 mbd.constructorArgumentValues。
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
int minNrOfArgs = cargs.getArgumentCount();
for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
if (valueHolder.isConverted()) {
resolvedValues.addIndexedArgumentValue(index, valueHolder);
} else {
// 核心代码就这么一句,将参数解析全部委托给valueResolver
Object resolvedValue =
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
ConstructorArgumentValues.ValueHolder resolvedValueHolder =
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
resolvedValueHolder.setSource(valueHolder);
resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
}
}
// 泛型参数解析 ...
return minNrOfArgs;
}
说明: resolveConstructorArguments 方法的核心代码就一句,valueResolver.resolveValueIfNecessary。看到 BeanDefinitionValueResolver 你是否会感到很熟悉呢?在 setter 等注入时,调用 beanFactory#applyPropertyValues 方法也需要解析 mbd.pvs 参数,同样也会用到 valueResolver.resolveValueIfNecessary 来解析配置参数,不管是那种注入方式,参数的解析都是一样的。
5.2 createArgumentArray
createArgumentArray 方法很长,其实逻辑反而并不复杂,遍历构造器的所有参数,先到 resolveConstructorArguments 方法已经解析过的参数中查找参数,如果查找到就使用配置的参数。当然如果查找不到,这时就判断是否允许自行注入,如果允许,则从 Spring IoC 容器中查找依赖,即调用 beanFactory#resolveDependency 方法。
/**
* @param resolvedValues 表示bd.constructorArgumentValues中已经解析的参数
* @param paramTypes 表示当前的构造器或方法的参数类型列表
* @param paramNames 方法的参数名称
* @param executable 方法或构造器
* @param autowiring 是否采用构造器自动注入,如果是则会从Spring IoC容器中查找参数
* @param fallback 如果已经有多个构造器或方法匹配,则fallback=false
*/
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
Class<?> paramType = paramTypes[paramIndex];
String paramName = (paramNames != null ? paramNames[paramIndex] : "");
// 1. 从默认的配置参数中查找参数
ConstructorArgumentValues.ValueHolder valueHolder = null;
if (resolvedValues != null) {
// 1.1 查找已经有参数,可以根据参数的索引或名称查找
valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
// 1.2 如果不是自动注入,则要想方设法查找参数,因为自动注入还可以从容器中查找参数
if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
}
}
// 2. 已经从配置该参数,这部分代码省略
if (valueHolder != null) {
...
// 3. 没有配置该参数,尝试从Spring IoC容器中查找
} else {
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
if (!autowiring) {
throw new UnsatisfiedDependencyException();
}
// 3.1 核心代码,调用 beanFactory#resolveDependency
Object autowiredArgument = resolveAutowiredArgument(
methodParam, beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
// 3.2 自动注入标记位
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
args.resolveNecessary = true;
}
}
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
}
return args;
}
说明: 对于构造器中的参数,如果在配置参数中命中,使用配置的参数。对于没有命中的参数,如果允许自动注入,则调用 beanFactory#resolveDependency 从容器中查找。
5.3 resolvePreparedArguments
resolvePreparedArguments 方法是对 mbd.preparedConstructorArguments 中还需要解析的参数调用 BeanDefinitionValueResolver#resolveValueIfNecessary 进行解析。
private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, Executable executable, Object[] argsToResolve, boolean fallback) {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
Class<?>[] paramTypes = executable.getParameterTypes();
Object[] resolvedArgs = new Object[argsToResolve.length];
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
Object argValue = argsToResolve[argIndex];
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
// createArgumentArray 方法设置的自动注入标记
if (argValue == autowiredArgumentMarker) {
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, fallback);
} else if (argValue instanceof BeanMetadataElement) {
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
} else if (argValue instanceof String) {
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
}
Class<?> paramType = paramTypes[argIndex];
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
}
return resolvedArgs;
}
说明: 核心就是 BeanDefinitionValueResolver#resolveValueIfNecessary,其实所以的类型注入最终都逃不离 beanFactory#resolveDependency 这个最基础的方法,而这个方法的核心是 Spring 的类型自省。
6. instantiateUsingFactoryMethod
其实也了解了构造器实例化 autowireConstructor 之后,instantiateUsingFactoryMethod 方法基本大同小异。同样是匹配工厂方法,解析参数。
每天用心记录一点点。内容也许不重要,但习惯很重要!
posted on 2020-02-20 19:41 binarylei 阅读(2263) 评论(1) 编辑 收藏 举报