吃透Spring源码(九):Spring实例化(createBeanInstance)源码解析

2023-10-30

一,createBeanInstance()方法概述

createBeanInstance 是Spring实例化的核心代码,它根据不同的情况会调用四种实例化方法:

  • obtainFromSupplier():通过Supplier实例化。
  • instantiateUsingFactoryMethod():通过工厂方法实例化。
  • autowireConstructor():用合适的构造函数实例化。
  • instantiateBean():用无参构造函数实例化。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    
		// 确认需要创建的bean实例的类可以实例化
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		// 确保class不为空,并且访问权限是public
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		/**
		 * ----------1,通过Supplier实例化------------
		 */
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
    
		/**
		 * ----------2,通过工厂方法实例化------------
		 */
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

    	/**
		 * ----------3,用合适的构造函数实例化------------
		 *
		 *  一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器。
		 *  在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,
		 *  避免再次创建相同bean时再次解析
		 */

		// 标记下,防止重复创建同一个bean
		boolean resolved = false;
		// 是否需要自动装配,构造有参数的需要
		boolean autowireNecessary = false;
		// 如果没有参数
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				// 一个类中有多个构造函数,每个构造函数都有不同的参数,所以调用前需要先根据参数锁定构造函数或对应的工厂方法
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		// 有构造参数的或者工厂方法
		if (resolved) {
			// 构造器有参数
			if (autowireNecessary) {
				// 构造函数自动注入
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				// 使用默认构造函数构造
				return instantiateBean(beanName, mbd);
			}
		}

		// 从bean后置处理器中为自动装配寻找构造方法
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// 找出最合适的默认构造方法
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			// 构造函数自动注入
			return autowireConstructor(beanName, mbd, ctors, null);
		}

    	/**
		 * ----------4,使用默认构造函数构造------------
		 */
		return instantiateBean(beanName, mbd);
	}

以上源码主要包括:

  1. 如果RootBeanDefinition中存在Supplier接口实例,则使用Supplier接口回调来实例化。
  2. 如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。如果一个类中中的方法被 @Bean注解修饰,那么Spring则会将其封装成一个 ConfigurationClassBeanDefinition。此时 factoryMethodName 也被赋值。所以也会调用instantiateUsingFactoryMethod 方法通过反射完成方法的调用,并将结果注入Spring容器中。
  3. 当以上两种都没有配置时,Spring则打算通过bean的构造函数来创建bean。首先会判断是否有缓存,即构造函数是否已经被解析过了, 因为一个bean可能会存在多个构造函数,这时候Spring会根据参数列表的来判断使用哪个构造函数进行实例化。但是判断过程比较消耗性能,所以Spring将判断好的构造函数缓存到RootBeanDefinition 中的 resolvedConstructorOrFactoryMethod 属性中。
  4. 如果缓存,则不需要解析,直接调用 autowireConstructor 或者 instantiateBean 方法创建bean。有参构造调用 autowireConstructor 方法,无参构造调用 instantiateBean 方法。
  5. 否则需要进行先进行解析,这里通过 determineConstructorsFromBeanPostProcessors 方法调用了 SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 的后处理器方法来进行解析,Spring 默认的实现在AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors 方法中。
  6. 获取解析后的候选的构造函数列表 ctors 后(最终的构造函数就从这个列表中选取),开始调用 autowireConstructor 或者 instantiateBean 方法创建bean。在autowireConstructor 中,进行了候选构造函数的选举,选择最合适的构造函数来构建bean,如果缓存已解析的构造函数,则不用选举,直接使用解析好的构造来进行bean的创建。

二,obtainFromSupplier()通过Supplier实例化

	/**
	 * 从supplier获取bean
	 */
	protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
		Object instance;

		// 获取原先创建的beanName
		String outerBean = this.currentlyCreatedBean.get();
		// 用当前作对做替换
		this.currentlyCreatedBean.set(beanName);
		try {
			// 调用supplier的方法
			instance = instanceSupplier.get();
		}
		finally {
			if (outerBean != null) {
				this.currentlyCreatedBean.set(outerBean);
			}
			else {
				this.currentlyCreatedBean.remove();
			}
		}

		// 如果没有创建对象,默认为NullBean
		if (instance == null) {
			instance = new NullBean();
		}
		// 初始化BeanWrapper并返回
		BeanWrapper bw = new BeanWrapperImpl(instance);
		initBeanWrapper(bw);
		return bw;
	}

Supplier方式比较简单,instanceSupplier.get()回调到自己定义的函数里面返回一个实例对象然后包装成BeanWrapperImpl返回就行了。

三,instantiateUsingFactoryMethod()通过工厂方法实例化。

如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。

这个源码太长,也并不重要,就不在这里展示了。简单来说,这里可以分为两种情况:

  1. 在 xml配置中,可以使用 factory-beanfactory-method 两个标签可以指定一个类中的方法,Spring会将这个指定的方法的返回值作为bean返回(如果方法是静态方法,则可以不创建factory-bean就直接调用,否则需要先将factory-bean注入到Spring中)。
  2. @Bean 注解的解析。在 ConfigurationClassPostProcessor 后处理器中,会对被 @Bean 注解修饰的方法进行解析,生成一个 ConfigurationClassBeanDefinitionBeanDefinition。此时BeanDefinitionfactoryMethodName 正是 @Bean修饰的方法本身。所以这里会调用 instantiateUsingFactoryMethod 方法。通过回调的方式调用 @Bean修饰的方法。并将返回结果注入到Spring容器中。

四,autowireConstructor():用合适的构造函数实例化

这个代码量也非常大,实现的功能实现上比较复杂,功能上却可以一句话讲清:简单来说,就是根据传入的参数列表,来匹配到合适的构造函数进行bean 的创建。

	public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
		// 实例化BeanWrapper。是包装bean的容器
		BeanWrapperImpl bw = new BeanWrapperImpl();
		this.beanFactory.initBeanWrapper(bw);

		Constructor<?> constructorToUse = null;
		ArgumentsHolder argsHolderToUse = null;
		Object[] argsToUse = null;

		// explicitArgs通过getBean方法传入
		// 如果getBean方法调用的时候指定方法参数那么直接使用
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			// 如果在调用getBean方法的时候没有指定,则尝试从配置文件中解析
			Object[] argsToResolve = null;
			synchronized (mbd.constructorArgumentLock) {
				// 尝试从缓存中获取
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached constructor...
					// 从缓存中找到了构造器,那么继续从缓存中寻找缓存的构造器参数
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						// 没有缓存的参数,就需要获取配置i文件中配置的参数
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			// 如果缓存中没有缓存的参数的话,即argsToResolve不为空,就需要解析配置的参数
			if (argsToResolve != null) {
				// 解析参数类型,比如将配置的String类型转换为list、boolean等类型
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
			}
		}

		// 如果没有缓存,就需要从构造函数开始解析
		if (constructorToUse == null || argsToUse == null) {
			// Take specified constructors, if any.
			// 如果传入的构造器数组不为空,就使用传入的过后早期参数,否则通过反射获取class中定义的构造器
			Constructor<?>[] candidates = chosenCtors;
			if (candidates == null) {
				Class<?> beanClass = mbd.getBeanClass();
				try {
					// 使用public的构造器或者所有构造器
					candidates = (mbd.isNonPublicAccessAllowed() ?
							beanClass.getDeclaredConstructors() : beanClass.getConstructors());
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Resolution of declared constructors on bean Class [" + beanClass.getName() +
							"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
				}
			}

			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;
				}
			}

			// Need to resolve the constructor.
			boolean autowiring = (chosenCtors != null ||
					mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
			ConstructorArgumentValues resolvedValues = null;

			int minNrOfArgs;
			if (explicitArgs != null) {
				minNrOfArgs = explicitArgs.length;
			}
			else {
				// 提取配置文件中的配置的构造函数参数
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				// 用于承载解析后的构造函数参数的值
				resolvedValues = new ConstructorArgumentValues();
				// 能解析到的参数个数
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}

			// 排序给定的构造函数,public的构造函数优先,参数数量降序
			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) {
					// Already found greedy constructor that can be satisfied ->
					// do not look any further, there are only less greedy constructors left.
					break;
				}
				if (parameterCount < minNrOfArgs) {
					// 参数个数不相等
					continue;
				}

				ArgumentsHolder argsHolder;
				Class<?>[] paramTypes = candidate.getParameterTypes();
				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) {
						if (logger.isTraceEnabled()) {
							logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
						}
						// Swallow and try next constructor.
						if (causes == null) {
							causes = new LinkedList<>();
						}
						causes.add(ex);
						continue;
					}
				}
				else {
					// Explicit arguments given -> arguments length must match exactly.
					if (parameterCount != explicitArgs.length) {
						continue;
					}
					// 构造函数没有参数的情况
					argsHolder = new ArgumentsHolder(explicitArgs);
				}

				// 探测是否有不确定性的构造函数存在,例如不同构造函数的参数为父子关系
				int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
						argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
				// Choose this constructor if it represents the closest match.
				// 如果它代表着当前最接近的匹配则选择作为构造函数
				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);
				}
			}

			if (constructorToUse == null) {
				if (causes != null) {
					UnsatisfiedDependencyException ex = causes.removeLast();
					for (Exception cause : causes) {
						this.beanFactory.onSuppressedException(cause);
					}
					throw ex;
				}
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Could not resolve matching constructor " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
			}
			else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Ambiguous constructor matches found in bean '" + beanName + "' " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
						ambiguousConstructors);
			}

			if (explicitArgs == null && argsHolderToUse != null) {
				// 将解析的构造函数加入缓存
				argsHolderToUse.storeCache(mbd, constructorToUse);
			}
		}

		Assert.state(argsToUse != null, "Unresolved constructor arguments");
		// 将构造的实例加入BeanWrapper中
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}

简单理一下上面的逻辑:

  1. 首先判断 explicitArgs 是否为空,如果不为空,则就直接使用 explicitArgs 作为构造函数的参数。
    explicitArgs 所代表的意思是 调用getBean方法是的传参,如下:在这里插入图片描述

  2. 如果 explicitArgs 为空,则尝试从缓存中获取,也即是从 RootBeanDefinitionresolvedConstructorArguments 属性或 preparedConstructorArguments 属性中获取。resolvedConstructorArguments 代表完全解析好的参数, preparedConstructorArguments 代表尚未完全解析的参数,如果 获取到 preparedConstructorArguments ,则需要在进一步的解析。

  3. 如果缓存中也没有获取到,则只能自己开始分析来获取候选构造函数列表,关于候选构造函数的信息,在调用该方法时就已经传递了过来,即Constructor<?>[] chosenCtors,如果 Constructor<?>[] chosenCtors 为null,则通过反射获取候选构造函数列表 candidates

  4. 获取到候选构造函数列表 candidates后,则会优先判断获取到的 candidates 是否只有一个构造函数,如果只要一个,则不需要解析,直接保存相关信息即解析完毕。

  5. 否则则进行候选构造函数列表candidates的选举,寻找最合适的构造函数,对 candidates 按照 public 构造函数优先参数数量降序,非public构造函数参数数量降序 规则排序,目的是为了后面检索的时候可以更快速判断是否有合适的构造函数。

  6. 排序结束后 ,开始遍历构造函数,按照 构造函数的参数类型和数量与构造函数一一匹配,寻找差异性最小的构造函数作为最终的构造函数并通过 cglib 或者 反射来 创建bean。


按照功能划分,整个 autowireConstructor 方法可以划分为四步:

  1. 解析构造函数参数。
  2. 获取候选的构造函数列表。
  3. 解析构造函数参数个数。
  4. 寻找最匹配的构造函数

1,解析构造函数参数

		// explicitArgs通过getBean方法传入
		// 如果getBean方法调用的时候指定方法参数那么直接使用
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			// 如果在调用getBean方法的时候没有指定,则尝试从配置文件中解析
			Object[] argsToResolve = null;
			synchronized (mbd.constructorArgumentLock) {
				// 尝试从缓存中获取
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached constructor...
					// 从缓存中找到了构造器,那么继续从缓存中寻找缓存的构造器参数
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						// 没有缓存的参数,就需要获取配置i文件中配置的参数
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			// 如果缓存中没有缓存的参数的话,即argsToResolve不为空,就需要解析配置的参数
			if (argsToResolve != null) {
				// 解析参数类型,比如将配置的String类型转换为list、boolean等类型
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
			}
		}

上面的逻辑还是很清楚的:

  • 如果有传入参数 explicitArgs,则直接使用 explicitArgs
  • 如果没有传入,尝试从缓存中获取
  • 如果参数完全解析了,则直接使用,如果没有则调用 resolvePreparedArguments 进行解析。

这里解释一下 resolvePreparedArguments 方法的作用。

我们声明的构造函数的可能是这样的:

	public ConstructorDemoA(Integer name) {
        this.name = name;
    }

但是我们在配置的时候配置文件却是这样的:

	<bean id="constructorDemoA" class="com.kingfish.springbootdemo.constructor.ConstructorDemoA">
        <constructor-arg index="0" value="666" ></constructor-arg>
    </bean>

这时候,Spring就需要有一个过程,从Spring 的 “666” 到 Integer 的 666 的转变,这个方法就是做类型转化的工作。但需要注意调用这个方法的前提条件是 argsToResolve != null。

2,获取候选的构造函数列表

			// 如果传入的构造器数组不为空,就使用传入的过后早期参数,否则通过反射获取class中定义的构造器
			Constructor<?>[] candidates = chosenCtors;
			if (candidates == null) {
				Class<?> beanClass = mbd.getBeanClass();
				try {
					// 获取bean的构造函数
					candidates = (mbd.isNonPublicAccessAllowed() ?
							beanClass.getDeclaredConstructors() : beanClass.getConstructors());
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Resolution of declared constructors on bean Class [" + beanClass.getName() +
							"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
				}
			}

			// 如果构造函数只有一个 & getBean 没有传参 & 构造参数无参
			// 满足上述三个条件,则无需继续筛选,直接创建 BeanWrapper 并返回即可。
			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;
				}
			}

这个逻辑也是比较清楚的 chosenCtors 是传入的构造函数列表:

  • 外部是否传入了候选构造函数列表( chosenCtors == null)
  • 如果没传入(chosenCtors 为null),通过反射获取构造函数列表
  • 如果构造函数只有一个 & getBean 没有传参 & 构造参数无参,则直接使用这唯一一个构造函数并返回

这里需要注意点是 传入的 chosenCtors ,在不同的调用场景下可能会传入null,或者 调用 SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 方法返回的值。Spring 默认的实现是在 AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors 方法中。

3,解析构造函数参数个数

			// 解析出来的构造函数的个数
			int minNrOfArgs;
			// 如果explicitArgs不为空,直接使用它作为参数,毕竟是传入的参数,没必要再从进一步解析。
			if (explicitArgs != null) {
				minNrOfArgs = explicitArgs.length;
			}
			else {
				// 提取配置文件中的配置的构造函数参数
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				// 用于保存解析后的构造函数参数的值,在resolveConstructorArguments中可以看到他的作用
				resolvedValues = new ConstructorArgumentValues();
				// 最终解析到的构造函数参数个数
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}

在 Spring 中指定的构造函数会保存在 RootBeanDefinition.constructorArgumentValues 中,类型为 ConstructorArgumentValues,如下。可以看到 ConstructorArgumentValues 分为两部分保存参数。

public class ConstructorArgumentValues {
	// 按照顺序声明的参数列表
	private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();
	// 按照类型声明的参数列表
	private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
	...
}

如下的定义中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="constructorDemoA" class="com.kingfish.springbootdemo.constructor.ConstructorDemoA">
        <constructor-arg index="0" ref="constructorDemoB"></constructor-arg>
        <constructor-arg index="1" value="666" ></constructor-arg>
        <constructor-arg value="999" ></constructor-arg>
    </bean>
    <bean id="constructorDemoB" class="com.kingfish.springbootdemo.constructor.ConstructorDemoB"></bean>
</beans>

constructorDemoB,666就被保存到 indexedArgumentValues 中, 999 就被保存到genericArgumentValues ,如下图

在这里插入图片描述

但是需要注意的是 这里面保存的是ValueHolder 类型,里面保存的也并不是 实际类型,而是未经转换的类型,即constructorDemoB 保存的并不是 ConstructorDemoB类 实例,而是保存了一个 beanName 为 constructorDemoB。这里的 666 保存的也是字符串形式(而实际的构造函数需要的是Integer形式)。总的来说就是 mbd.getConstructorArgumentValues(); 中的构造函数值并不一定是真正可以使用的类型,还需要进行一个解析进行类型的匹配。

而这个解析过程就发生在 resolveConstructorArguments 方法中。如下:

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();
		// 遍历 indexedArgumentValues 
		for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
			int index = entry.getKey();
			if (index < 0) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Invalid constructor argument index: " + index);
			}
			// 这里注意,如果 <constructor-arg> 的index属性大于 参数实际个数,那么Spring会采用index属性的值
			if (index > minNrOfArgs) {
				// +1 是因为index 从0开始
				minNrOfArgs = index + 1;
			}
			ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
			// 如果类型已经解析过,则保存在 resolvedValues 中
			if (valueHolder.isConverted()) {
				resolvedValues.addIndexedArgumentValue(index, valueHolder);
			}
			else {
				// 否则进行类型解析后再保存到 resolvedValues 中
				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);
			}
		}
		// 遍历 genericArgumentValues 
		for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
			// 如果已经解析,则保存到resolvedValues 中
			if (valueHolder.isConverted()) {
				resolvedValues.addGenericArgumentValue(valueHolder);
			}
			else {
				// 否则进行类型解析后再保存到 resolvedValues 中
				Object resolvedValue =
						valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
				ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
						resolvedValue, valueHolder.getType(), valueHolder.getName());
				resolvedValueHolder.setSource(valueHolder);
				resolvedValues.addGenericArgumentValue(resolvedValueHolder);
			}
		}
		// 返回解析后的构造函数参数个数。
		return minNrOfArgs;
	}

4,寻找最匹配的构造函数

代码比较长,上面已经贴出了完整版,这里就简化一下:

	...
	// 排序构造函数,方便后面检索
	AutowireUtils.sortConstructors(candidates);
	// 差异度,最后选择minTypeDiffWeight  最小的作为最匹配的构造函数
	int minTypeDiffWeight = Integer.MAX_VALUE;
	Set<Constructor<?>> ambiguousConstructors = null;
	LinkedList<UnsatisfiedDependencyException> causes = null;
	// 筛选构造函数,根据参数数量,参数类型匹配
	for (Constructor<?> candidate : candidates) {
	
		... 
		
		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) {
				if (logger.isTraceEnabled()) {
					logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
				}
				// Swallow and try next constructor.
				if (causes == null) {
					causes = new LinkedList<>();
				}
				causes.add(ex);
				continue;
			}
		}
		else {
			// Explicit arguments given -> arguments length must match exactly.
			if (parameterCount != explicitArgs.length) {
				continue;
			}
			argsHolder = new ArgumentsHolder(explicitArgs);
		}
		
		...
		
		if (explicitArgs == null && argsHolderToUse != null) {
			// 将解析出来的信息缓存到RootBeanDefinition中
			argsHolderToUse.storeCache(mbd, constructorToUse);
		}
	}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
// 创建bean,并保存
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));

这一步的目的就是根据参数数量和参数列表来选择最合适的构造函数,并且调用 instantiate(beanName, mbd, constructorToUse, argsToUse) 方法来创建bean实例。

下面提两点:

  1. 由于在配置文件中声明bean不仅仅可以使用参数位置索引的方式创建,也支持通过参数名称设定参数值的情况,如下:

    <constructor-arg name="constructorDemoB" ref="constructorDemoB"></constructor-arg>
    

    所以这时候,就需要首先确定构造函数中的参数名称。而获取参数名的方式有两种,一种是通过注解直接获取(@ConstructorProperties 注解获取),即上面代码中对应的 ConstructorPropertiesChecker.evaluate(candidate, parameterCount);,另一种是通过Spring同构的工具类 ParameterNameDiscoverer ,这个在代码中也有使用。
    完成这一步的时候,构造函数、参数名称、参数类型、参数值都确定后就可以锁定构造函数以及转换对应的参数类型了。

  2. instantiate 方法也很简单,根据 beanFactory 中的 bean实例化策略来实例化对象:

    private Object instantiate(
    			String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) {
    
    		try {
    			// 获取实例化策略
    			InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
    			// 通过策略实例化bean
    			if (System.getSecurityManager() != null) {
    				return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
    						strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
    						this.beanFactory.getAccessControlContext());
    			}
    			else {
    				return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
    			}
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    					"Bean instantiation via constructor failed", ex);
    		}
    	}
    

    注:关于 实例化策略,主要两种 SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy,简单实例化策略(直接反射) 和 Cglib 动态代理策略(通过cglib 代理),默认第二种。

五,无参构造函数实例化

相较于上面的有参构造函数,无参构造函数的解析就简单很多:

	protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
		try {
			Object beanInstance;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged(
						(PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
						getAccessControlContext());
			}
			else {
				// 获取实例化策略并且进行实例化操作
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
			}
			// 包装成BeanWrapper
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
		}
	}
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		// bd对象定义中,是否包含MethodOverride列表,spring中有两个标签参数会产生MethodOverrides,分别是lookup-method,replaced-method
		// 没有MethodOverrides对象,可以直接实例化
		if (!bd.hasMethodOverrides()) {
			// 实例化对象的构造方法
			Constructor<?> constructorToUse;
			// 锁定对象,使获得实例化构造方法线程安全
			synchronized (bd.constructorArgumentLock) {
				// 查看bd对象里使用否含有构造方法
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				// 如果没有
				if (constructorToUse == null) {
					// 从bd中获取beanClass
					final Class<?> clazz = bd.getBeanClass();
					// 如果要实例化的beanDefinition是一个接口,则直接抛出异常
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						// 获取系统安全管理器
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							// 获取默认的午餐构造器
							constructorToUse = clazz.getDeclaredConstructor();
						}
						// 获取到构造器之后将构造器赋值给bd中的属性
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			// 通过反射生成具体的实例化对象
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			// 必须生成cglib子类
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

这里可以用一句话概括 : 是否有方法被覆盖(是否使用replace 或 lookup 进行配置),有则使用cglib动态代理,增加方法,否则直接通过反射创建。

六,总结

AbstractAutowireCapableBeanFactory#createBeanInstance方法处于Spring 创建bean 的入口阶段,完成了bean 的初步创建,调用各种扩展接口来尝试完成bean的创建(Supplier、factory-method),失败了则根据传入参数和和构造函数列表来选择合适的构造函数来创建bean。

但是并未完成属性注入、接口特性实现(如 Aware)、标签设置(如inti-method)的设置。在后续的 AbstractAutowireCapableBeanFactory#populateBean 方法中完成了属性的注入。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

吃透Spring源码(九):Spring实例化(createBeanInstance)源码解析 的相关文章

  • 工具--Typora详解

    工具 Typora详解 零 文章目录 一 MarkDown 1 MarkDown是什么 Markdown 是一种轻量级标记语言 它允许人们使用易读易写的纯文本格式编写文档 Markdown 语言在 2004 由约翰 格鲁伯 英语 John
  • opengles reference card

    https www khronos org files opengles31 quick reference card pdf https www khronos org opengles sdk docs reference cards
  • mac系统ssh文件位置

    open ssh 转载于 https www cnblogs com thinkingthigh p 8874204 html
  • js的数字和字符串区分不开问题

    我们在开发的时候经常会出现 if this name 1 执行对应逻辑 但是就是在这个判断的时候 就是不知道该写成 if this name 1 执行对应逻辑 还是写成 if this name 1 执行对应逻辑 这是一个坑 代码调试时候遇
  • 第十四届蓝桥杯模拟赛(第三期)——Java版

    第一题 请找到一个大于 2022 的最小数 这个数转换成十六进制之后 所有的数位 不含前导 0 都为字母 A 到 F 请将这个数的十进制形式作为答案提交 public class Main public static void main S
  • MacBook m1pro在conda环境关于架构出现过的问题

    回想一下十月份的时候刚拿到电脑做了点啥 刚开始没有进行转换架构的虚拟环境的设置 导致好像是安装pyqt5 一直失败 总之查了半天 最后指向似乎是架构问题 然后利用https www bilibili com read cv13742031
  • vue3项目引入typescript总结

    tsconfig json详细配置 根选项 include 指定被编译文件所在的目录 exclude 指定不需要被编译的目录 extends 指定要继承的配置文件 files 指定被编译的文件 references 项目引用 是 TS 3
  • 使用python-pyhdfs连接hdfs时报错

    一 问题描述 raise ConnectionError e request request ConnectionError HTTPConnectionPool host a port 50075 Max retries exceeded
  • python爬虫js逆向学习(三)

    1 问题分析 1 1 查询条件设置后进行点击事件 可抓取到ajax请求的获取的数据包 1 2 对数据包请求过程进行分析 发现Formdata及respopnse都是加密的且formdata中的参数每次刷新后都不同 1 3 既然参数及相应数据
  • 哈希桶的实现

    上一篇博客中介绍了用闭散列法的二次探测和开链法构造哈希表的原理即实现方式 构造哈希表的闭散列法之二次探测地址 http blog csdn net qq 36221862 article details 73488162 下面介绍另一种方法
  • QT__TCP

    QTcpSocket断开自动重新连接 auto connect after disconnected 转载于 http blog csdn net owldestiny article details 8452605 QTcpSocket断
  • 小程序引入vant-weapp

    小程序引入第3方样式库
  • 灰度直方图OpenCV

    recognition cpp 此文件包含 main 函数 程序执行将在此处开始并结束 include pch h include
  • Java实现判断是否为最新版本方法

    判断是否为最新版本方法 将版本号根据 切分为int数组 比较 param localVersion 本地版本号 param onlineVersion 线上版本号 return 是否为新版本 throws IllegalArgumentEx
  • NumPy模块:Python科学计算神器之一

    欢迎来到我的博客 作者 秋无之地 简介 CSDN爬虫 后端 大数据领域创作者 目前从事python爬虫 后端和大数据等相关工作 主要擅长领域有 爬虫 后端 大数据开发 数据分析等 欢迎小伙伴们点赞 收藏 留言 关注 关注必回关 上一篇文章已
  • nginx反向代理(前端 开发环境、测试环境、生产环境 解决方案)

    什么是Nginx Nginx engine x 是一个高性能的HTTP和反向代理服务 也是一个IMAP POP3 SMTP服务 Nginx是由伊戈尔 赛索耶夫为俄罗斯访问量第二的Rambler ru站点 俄文 开发的 第一个公开版本0 1
  • 团队耗时半年,整理两份非常夯实算法工程师基本功。

    这几年来 圈子内越来越卷的话题持续不下 再加上大厂程序员 被毕业 再就业 的新闻层出不穷 贩卖给人们的焦虑也越来越多 2016年 深度学习的春天是不是要来了 2017年 人工智能是不是一个泡沫 2018年 算法岗是否值得进入 2019年 如
  • pandas使用datetime作为索引并用groupby调用tseries.offsets时间位移分组

    import numpy as np import pandas as pd from datetime import datetime from pandas tseries offsets import Day MonthEnd sj
  • 路由控制配置network命令解析

    network命令 1 命令功能 network命令用来配置BGP将IP路由表中的路由以静态方式加入到BGP路由表中并发布给对等体 undo network命令用来删除指定的以静态方式加入到BGP路由表中的路由 缺省情况下 BGP不将IP路

随机推荐

  • C# 获取系统Icon、获取文件相关的Icon

    1 获取系统Icon 工具下载SystemIcon exe using System using System Collections Generic using System ComponentModel using System Dat
  • 什么是DDoS攻击?

    DDoS攻击是目前最常见的网络攻击方式之一 其见效快 成本低的特点 让DDoS这种攻击方式深受不法分子的喜爱 DDoS攻击经过十几年的发展 已经 进化 的越来越复杂 黑客不断升级新的攻击方式以便于绕过各种安全防御措施 一 什么是DDoS攻击
  • QT实例 - 实现http通信

    QT实现通过HTTP与服务器进行交互 原文链接 https blog csdn net hwc3737 article details 108367037 添加依赖 在项目的 pro文件中添加 QT network 引入相关头文件 incl
  • 生命在于学习——指纹混淆技术学习

    一 前言 本篇文章仅为学习笔记记录 不得用于违规用途 本篇文章为安全社公众号的Poker安全所发 本文仅为学习复现 二 介绍 指纹混淆技术 顾名思义 就是迷惑指纹扫描识别技术 三 思路 作者的思路 1 伪装CMS 作者第一个想到的就是wor
  • python的包

    什么是模块 xxx py文件 社么是包 多个模块组成的文件夹 为啥要使用模块 让我下次直接使用 不需要再重写 或者方便多人开发 1 新建一个文件夹testModel 在此文件夹中创建一个名为 init py的文件 此时python解释器就认
  • 第一章:基本概念

    什么是数据结构 其实官方没有统一定义 数据结构是数据对象 以及存在于该对象的实例和组成实例的数据元素之间的各种联系 这种联系可以通过定义相关的函数给出 Sartaj Sahni 数据结构 算法与应用 数据结构是ADT 抽象数据类型 Abst
  • 在服务器上搭建git仓库

    在本地项目中导出裸仓库 git clone bare project name git 上传到服务器上pscp r project name git user name ip or hostname git path 在本地仓库中设置服务端
  • 蓝桥杯 算法训练 印章

    蓝桥杯 算法训练 印章 共有n种图案的印章 每种图案的出现概率相同 小A买了m张印章 求小A集齐n种印章的概率 输入输出 一行两个正整数n和m 一个实数P表示答案 保留4位小数 样例 2 3 0 7500 这是个dp问题 存在两个变量 印章
  • springboot基础篇—SpringBoot 配置

    1 配置文件 SpringBoot 使用一个全局配置文件 application yml application properties 配置文件放在 src main resources 目录或者 类路径 config 下 yml 是 YA
  • 【Spring Boot丨(11 )】json的集成

    集成JSON 概述 Jackson Gson JSON B 主页传送门 传送 概述 Spring boot 提供了三种json库的集成 Gson Jackson JSON B 上述三种库提供了将Java对象转换为JSON字符串以及将JSON
  • c语言全局变量fork,使用fork进行C语言编程()

    好吧我做错了什么 我在Ubuntu上这样做 我想让系统命令 ls 和一个参数如 a 然后让孩子执行它 然后父母只是打印出来 我不明白为什么我一直让 父母 返回两次 有任何想法吗 使用fork进行C语言编程 include include i
  • 内存四区(代码区 静态区 栈区 堆区)

    参考 内存四区 代码区 静态区 栈区 堆区 作者 今天天气眞好 发布时间 2021 04 01 18 09 13 网址 https blog csdn net qq 51118175 article details 115379779 sp
  • C#文件重命名工具

    文章目录 工具背景 4个文件介绍 RenamesSpecificPrefixFile exe config DataSave txt 工具介绍 重命名的存储方式 文件夹介绍 源文件夹 结果 使用 PDF 视频 重名时坚持拷贝 可能的报错 工
  • json数据一次读取多条数据(数组形式,数组前面没有字符和有字符)的操作方法

    适用于读取的数据如图所示的数组格式 public static List
  • 田忌赛马

    题目描述 我国历史上有个著名的故事 那是在2300年以前 齐国的大将军田忌喜欢赛马 他经常和齐王赛马 他和齐王都有三匹马 常规马 上级马 超级马 一共赛三局 每局的胜者可以从负者这里取得200银币 每匹马只能用一次 齐王的马好 同等级的马
  • Linux(CentOS6.5_X86.64)编译libjpeg出现“checking host system type... Invalid configuration `x86_64-unknow...

    本文地址http comexchan cnblogs com 作者Comex Chan 尊重知识产权 转载请注明出处 谢谢 今天在编译libjpeg 的时候 遇到下面的报错 checking host system type Invalid
  • 实现以太坊的数据结构----状态树

    状态树 实现账户地址 addr 到账户状态 state 的映射 在以太坊中账户地址用160位 bits 表示 即40个16进制的数 1 为什么不能使用哈希表实现 用哈希表实现 就是系统中的全节点维护一个哈希表 在不考虑哈希碰撞的情况下 每次
  • Flask - 实现数据分页

    目录 一 Flask SQLAlchemy 直接获取分页后的数据 1 0 基于 flsk sqlalchemy 的批量数据插入 add all list 1 1 Pagination对象的常用属性 1 2 Pagination对象的常用方法
  • nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件,很全

    文章目录 前言 一 nginx简介 1 什么是 nginx 和可以做什么事情 2 Nginx 作为 web 服务器 3 正向代理 4 反向代理 5 负载均衡 6 动静分离 二 Nginx 的安装 Linux centos为例 1 准备工作
  • 吃透Spring源码(九):Spring实例化(createBeanInstance)源码解析

    一 createBeanInstance 方法概述 createBeanInstance 是Spring实例化的核心代码 它根据不同的情况会调用四种实例化方法 obtainFromSupplier 通过Supplier实例化 instant