前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Ioc源码分析 之 Bean的加载(五):实例化Bean

Spring Ioc源码分析 之 Bean的加载(五):实例化Bean

作者头像
炳臣
修改2019-09-18 19:18:52
1.4K0
修改2019-09-18 19:18:52
举报
文章被收录于专栏:一块自留地一块自留地

上篇文章Spring Ioc源码分析 之 Bean的加载(四):createBean()中我们分析了doCreateBean()方法的大致流程,这篇文章我们就详细分析下实例化 beancreateBeanInstance()方法,剩下的步骤将在其他文章中介绍。

实例化 Bean

doCreateBean()代码 <2> 处,有一行代码instanceWrapper = createBeanInstance(beanName, mbd, args); 我们追踪进去看一下:

代码语言:javascript
复制
//AbstractAutowireCapableBeanFactory.java

//创建Bean的实例对象
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		//解析beanName 为 class
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		//检查确认Bean是可实例化的
		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());
		}
                 //如果存在 Supplier 回调,则使用给定的回调方法初始化策略
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
                //使用 FactoryBean 的 factory-method 来创建,支持静态工厂和实例工厂
		if (mbd.getFactoryMethodName() != null)  {
			//调用工厂方法实例化
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same 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) {
				//构造函数自动注入进行实例化
				//一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数进行 bean 的实例化
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				//使用默认的无参构造方法实例化
				return instantiateBean(beanName, mbd);
			}
		}

		// Need to determine the constructor...
		//需要根据参数解析构造函数
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			//使用容器的自动装配特性,调用匹配的构造方法实例化
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// No special handling: simply use no-arg constructor.
		//使用默认的无参构造方法实例化
		return instantiateBean(beanName, mbd);
	}
复制代码

这段代码中,Spring把Bean的实例话分为了4种方式:

  • Supplier 回调
  • 工厂方法初始化
  • 构造函数自动注入初始化
  • 默认无参构造方法初始化

1.1、 Supplier 回调

如果存在 Supplier 回调,则调用 obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) 方法,进行初始化。 Supplier是一个接口,定义如下:

代码语言:javascript
复制
public interface Supplier<T> {

    T get();
    
}
复制代码

这个接口有什么作用?用于指定创建 bean 的回调。如果我们设置了这样的回调,那么其他的构造器或者工厂方法都会没有用 设置的地方在BeanDefinition的构造函数中,如:

代码语言:javascript
复制
// RootBeanDefinition.java

public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {
	super();
	setBeanClass(beanClass);
	setScope(scope);
	// 设置 instanceSupplier 属性
	setInstanceSupplier(instanceSupplier);
}

1.2、工厂方法初始化

如果存在工厂方法,则使用工厂方法进行初始化。这部分代码非常长,很复杂,这里就不详细说了。

1.3、构造函数自动注入初始化

首先判断缓存,如果缓存中存在(resolved==true),即已经解析过了,则直接使用已经解析了的。否则,先解析构造函数,然后通过构造函数自动注入初始化。

1.3.1、autowireConstructor()

autowireConstructor() 这个初始化方法,我们可以简单理解为通过带有参数的构造方法,来初始化 Bean 对象。带有参数的实例化过程相当复杂,因为存在这不确定性,所以在判断对应参数上做了大量工作。 代码段如下:

代码语言:javascript
复制
//AbstractAutowireCapableBeanFactory.java

public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
		@Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {

	// 封装 BeanWrapperImpl 对象,并完成初始化
	BeanWrapperImpl bw = new BeanWrapperImpl();
	this.beanFactory.initBeanWrapper(bw);

	Constructor<?> constructorToUse = null;// 最终使用的构造函数
	ArgumentsHolder argsHolderToUse = null;// 构造参数
	Object[] argsToUse = null;// 构造参数

	// 判断有无显式指定参数,如果有则优先使用,如 xxxBeanFactory.getBean("teacher", "李华",3);
<1>	if (explicitArgs != null) {
		argsToUse = explicitArgs;
	}
	// 没有显式指定参数,则解析配置文件中的参数
<2>	else {
		Object[] argsToResolve = null;
		synchronized (mbd.constructorArgumentLock) {
			// 优先尝试从缓存中获取,spring对参数的解析过程是比较复杂也耗时的,所以这里先尝试从缓存中获取已经解析过的构造函数参数
			constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
                        //如果构造方法和参数都不为Null
			if (constructorToUse != null && mbd.constructorArgumentsResolved) {
				// Found a cached constructor...
				// 获取缓存中的构造参数
				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, constructorToUse, argsToResolve);
		}
	}

	// 缓存不存在,则需要解析构造函数参数,以确定使用哪一个构造函数来进行实例化
<3>	if (constructorToUse == null) {
		// Need to resolve the constructor.
		boolean autowiring = (chosenCtors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		// 用于承载解析后的构造函数参数的值
		ConstructorArgumentValues resolvedValues = null;

		//参数个数
<4>		int minNrOfArgs;
		if (explicitArgs != null) {
			minNrOfArgs = explicitArgs.length;
		}
		else {
			// 从 BeanDefinition 中获取构造参数,也就是从配置文件中提取构造参数
			ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
			resolvedValues = new ConstructorArgumentValues();
			// 能解析到的参数个数
			minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
		}

		// Take specified constructors, if any.
		//使用指定的构造函数,如果有的话
<5>		Constructor<?>[] candidates = chosenCtors;
		//没有
		if (candidates == null) {
			Class<?> beanClass = mbd.getBeanClass();
			try {
				//通过反射获取所有构造函数
				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);
			}
		}

		// 对所有构造函数进行排序,public 且 参数最多的构造函数会排在第一位
<6>		AutowireUtils.sortConstructors(candidates);
		int minTypeDiffWeight = Integer.MAX_VALUE;
		//模棱两可的构造函数集合
		Set<Constructor<?>> ambiguousConstructors = null;
		LinkedList<UnsatisfiedDependencyException> causes = null;

		// 迭代所有构造函数,解析确定使用哪一个构造函数
<7>		for (Constructor<?> candidate : candidates) {
			// 获取该构造函数的参数类型
<8>			Class<?>[] paramTypes = candidate.getParameterTypes();

			// 如果已经找到选用的构造函数或者需要的参数个数小于当前的构造函数参数个数,则终止。
			// 因为,已经按照参数个数降序排列了
			if (constructorToUse != null && argsToUse.length > paramTypes.length) {
				// Already found greedy constructor that can be satisfied ->
				// do not look any further, there are only less greedy constructors left.
				break;
			}
			// 参数个数不等,跳过
			if (paramTypes.length < minNrOfArgs) {
				continue;
			}

			// 参数持有者 ArgumentsHolder 对象
			ArgumentsHolder argsHolder;
<9>			if (resolvedValues != null) {
				try {
					// 获取注解上的参数名称 by @ConstructorProperties
					String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
					if (paramNames == null) {
						ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
						if (pnd != null) {
							// 获取指定构造函数的参数名称
							paramNames = pnd.getParameterNames(candidate);
						}
					}
					// 根据构造函数和构造参数,创建参数持有者 ArgumentsHolder 对象
					argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
							getUserDeclaredConstructor(candidate), autowiring);
				}
				catch (UnsatisfiedDependencyException ex) {
					if (this.beanFactory.logger.isTraceEnabled()) {
						this.beanFactory.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 (paramTypes.length != explicitArgs.length) {
					continue;
				}
				// 根据 getBean()传入的 explicitArgs ,创建 ArgumentsHolder 对象
				argsHolder = new ArgumentsHolder(explicitArgs);
			}

			//通过构造函数参数差异值对比,得出最适合使用的构造函数
				// isLenientConstructorResolution 判断解析构造函数的时候是否以宽松模式还是严格模式(默认宽松)
				// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常
				// 宽松模式:使用具有"最接近的模式"进行匹配
			int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
					argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
			// Choose this constructor if it represents the closest match.
			// 如果它代表着当前最接近的匹配则选择其作为构造函数
			//差异值越小,越匹配,每次和分数最小的去比较
<10>			if (typeDiffWeight < minTypeDiffWeight) {
				constructorToUse = candidate;
				argsHolderToUse = argsHolder;
				argsToUse = argsHolder.arguments;
				minTypeDiffWeight = typeDiffWeight;
				ambiguousConstructors = null;
			}
			// 如果两个构造方法与参数值类型列表之间的差异量一致,那么这两个方法都可以作为
	                // 候选项,这个时候就出现歧义了,这里先把有歧义的构造方法放入ambiguousConstructors
			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);
		}
            
                 // 将解析的构造函数、参数 加入缓存
<11>		if (explicitArgs == null) {
			/*
                	 * 缓存相关信息,比如:
                	 *   1. 已解析出的构造方法对象 resolvedConstructorOrFactoryMethod
                	 *   2. 构造方法参数列表是否已解析标志 constructorArgumentsResolved
                	 *   3. 参数值列表 resolvedConstructorArguments 或 preparedConstructorArguments
                	 *
                	 * 这些信息可用在其他地方,用于进行快捷判断
                	 */
			argsHolderToUse.storeCache(mbd, constructorToUse);
		}
	}

	try {
		//获取Bean的初始化策略
		final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
		Object beanInstance;

		//创建 Bean 对象
<12>		if (System.getSecurityManager() != null) {
			final Constructor<?> ctorToUse = constructorToUse;
			final Object[] argumentsToUse = argsToUse;
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
					beanFactory.getAccessControlContext());
		}
		else {
			beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
		}

		//设置到 bw 中
		bw.setBeanInstance(beanInstance);
		return bw;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(mbd.getResourceDescription(), beanName,
				"Bean instantiation via constructor failed", ex);
	}
	}
复制代码

代码很长,但不要慌,我们来一步步分析:

  • <1>处,判断有无显式指定构造参数
  • <2>处,没有显式指定参数,则从缓存中获取
  • <3>处,缓存不存在,解析构造函数参数
  • <4>处,获取构造参数个数
  • <5>处,获取所有构造方法
  • <6>处,对所有构造方法排序
  • <7>处,遍历所有构造方法
  • <8>处,通过参数校验构造方法
  • <9>处,创建参数持有者 ArgumentsHolder
  • <10>处,筛选出符合的构造方法
  • <11>处,将解析的构造函数、参数 加入缓存
  • <12>处,实例化Bean对象
1.3.1.1、判断有无显式指定构造参数
  • explicitArgs 外部传入的指定构造参数
  • argsToUse 要使用的构造参数

explicitArgs 是指外部传入的指定构造参数,例如xxxBeanFactory.getBean("teacher", "李华",3),(李华和3)就是传入的指定参数。 argsToUse 是我们实例化时要使用的构造参数,这里判断如果explicitArgs不为null的化,就把explicitArgs赋值给 argsToUse。

1.3.1.2、没有显式指定参数,则从缓存中获取
代码语言:javascript
复制
Object[] argsToResolve = null;
	synchronized (mbd.constructorArgumentLock) {
		// 优先尝试从缓存中获取,spring对参数的解析过程是比较复杂也耗时的,所以这里先尝试从缓存中获取已经解析过的构造函数参数
		constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
		//如果构造方法和参数都不为Null
		if (constructorToUse != null && mbd.constructorArgumentsResolved) {
			// Found a cached constructor...
			// 获取缓存中的构造参数
			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, constructorToUse, argsToResolve);
	}

首先从缓存中mbd.resolvedConstructorOrFactoryMethod获取构造方法,如果缓存中存在构造方法和参数,就解析构造参数。 因为缓存中的构造参数不一定是最终值,如给定方法的构造函数 A(int ,int ),则通过此方法后就会把配置文件中的("1","1")转换为 (1,1)

1.3.1.3、缓存不存在,解析构造函数参数

如果缓存不存在,则需要解析构造函数参数,以确定使用哪一个构造函数来进行实例化

1.3.1.4、获取构造参数个数
代码语言:javascript
复制
        //参数个数
	int minNrOfArgs;
	if (explicitArgs != null) {
		minNrOfArgs = explicitArgs.length;
	}
	else {
		// 从 BeanDefinition 中获取构造参数,也就是从配置文件中提取构造参数
		ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
		resolvedValues = new ConstructorArgumentValues();
		// 能解析到的参数个数
		minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
	}
  • 如果explicitArgs不为null 则直接获取。
  • 为null 需要解析保存在 BeanDefinition 构造函数中指定的参数 并获取能解析到的参数个数
1.3.1.5、获取所有构造方法

先尝试获取指定的构造方法,如果没有,则利用反射获取所有构造方法

1.3.1.6、对所有构造方法排序

排序的主要目的,是为了能够更加方便的找到最匹配的构造方法,因为构造方法的确认是根据参数个数确认的。排序的规则是:先按照 public / 非 public 构造方法升序,再按照构造参数数量降序。

1.3.1.7、遍历所有构造方法

遍历所有构造方法,筛选出最匹配的一个

1.3.1.8、通过参数校验构造方法
代码语言:javascript
复制
// 获取该构造函数的参数类型
	Class<?>[] paramTypes = candidate.getParameterTypes();

	///这里的判断构造方法和构造方法参数 都不是空,又由于之前对构造方法做了排序。所以在使用的参数的个数已经大于当前构造方法的参数个数的时候,实际上已经取到了想要的构造方法。
	if (constructorToUse != null && argsToUse.length > paramTypes.length) {
		// Already found greedy constructor that can be satisfied ->
		// do not look any further, there are only less greedy constructors left.
		break;
	}
	// 当前的构造参数个数小于我们要求的个数,跳过
	if (paramTypes.length < minNrOfArgs) {
		continue;
	}

这段代码也不复杂,第一个if是break分支,满足条件就跳出for循环,到这里就意为着找到了最匹配的构造方法。 EX: 假设现在有一组构造方法按照上面的排序规则进行排序,排序结果如下:

代码语言:javascript
复制
     1. public Hello(Object, Object, Object)
     2. public Hello(Object, Object)
     3. public Hello(Object)
     4. protected Hello(Integer, Object, Object, Object)
     5. protected Hello(Integer, Object, Object)
     6. protected Hello(Integer, Object)

由于是按降序排序的,所以会先去匹配构造方法1,发现 argsToUse.length > paramTypes.length

第二个if是快速判断当前构造方法是否符合我们的要求。

  • paramTypes 当前构造方法的参数个数
  • minNrOfArgs 我们要求的构造方法的参数个数 如果当前的构造参数个数小于我们要求的个数,说明当前构造方法不符合我们的要求,直接 continue
1.3.1.9、创建参数持有者 ArgumentsHolder
代码语言:javascript
复制
        // 参数持有者 ArgumentsHolder 对象
	ArgumentsHolder argsHolder;
	if (resolvedValues != null) {
		try {
			// 获取注解上的参数名称 by @ConstructorProperties
			String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
			if (paramNames == null) {
				// ParameterNameDiscoverer 是用于解析方法和构造函数的参数名称的接口,为参数名称探测器
				ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
				if (pnd != null) {
					// 获取指定构造函数的参数名称
					paramNames = pnd.getParameterNames(candidate);
				}
			}
			// 根据构造函数和构造参数,创建参数持有者 ArgumentsHolder 对象
			argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
					getUserDeclaredConstructor(candidate), autowiring);
		}
		catch (UnsatisfiedDependencyException ex) {
			if (this.beanFactory.logger.isTraceEnabled()) {
				this.beanFactory.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 (paramTypes.length != explicitArgs.length) {
			continue;
		}
		// 根据 getBean()传入的 explicitArgs ,创建 ArgumentsHolder 对象
		argsHolder = new ArgumentsHolder(explicitArgs);
	}

这里主要有两个逻辑:

  • resolvedValues != null 即没有显示指定构造参数
  • resolvedValues == null 即显示指定了构造参数

第一个分支: 先通过@ConstructorProperties注解获取构造参数名称,如果获取不到,再通过ParameterNameDiscoverer获取,最后创建 ArgumentsHolder 第二个分支: 直接使用显示传入的构造参数 explicitArgs 来 new 一个ArgumentsHolder

将参数包装成 ArgumentsHolder 对象。该对象用于保存参数,我们称之为参数持有者。在这个过程中再次解析构造参数,进行类型转换,如把配置文件中的string转换成需要的int。 当将对象包装成 ArgumentsHolder 对象后,我们就可以通过它来进行构造函数匹配。匹配分为严格模式和宽松模式:

  • 严格模式:解析构造函数时,必须所有参数都需要匹配,否则抛出异常。
  • 宽松模式:从模棱两可的构造方法中,选择最接近的。 判断的依据是根据 BeanDefinition 的 isLenientConstructorResolution 属性(该参数是我们在构造 AbstractBeanDefinition 对象是传递的)来获取类型差异权重(typeDiffWeight) 的。
1.3.1.10、筛选出符合的构造方法
代码语言:javascript
复制
//通过构造函数参数差异值对比,得出最适合使用的构造函数
		// isLenientConstructorResolution 判断解析构造函数的时候是否以宽松模式还是严格模式(默认宽松)
		// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常
		// 宽松模式:使用具有"最接近的模式"进行匹配
	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;
	}
	// 如果两个构造方法与参数值类型列表之间的差异量一致,那么这两个方法都可以作为
	// 候选项,这个时候就出现歧义了,这里先把有歧义的构造方法放入ambiguousConstructors
	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);
			}

先通过计算得出当前构造方法的差异值typeDiffWeight,每次和分数最小的去比较,筛选出差异值最小的,最终比较出一个最匹配的构造方法。 差异值大于最小差异值的,加入到候选集合ambiguousConstructors,我称之为模棱两可的构造方法,该集合在《宽松模式》下使用。

至此,所有构造方法都遍历完毕。如果仍没有筛选出构造方法,抛出异常。 如果模棱两可的构造方法不为空,但模式为 严格模式,则抛异常。

1.3.1.11、将解析的构造函数、参数 加入缓存
代码语言:javascript
复制
// 将解析的构造函数、参数 加入缓存
if (explicitArgs == null) {
	/*
	 * 缓存相关信息,比如:
	 *   1. 已解析出的构造方法对象 resolvedConstructorOrFactoryMethod
	 *   2. 构造方法参数列表是否已解析标志 constructorArgumentsResolved
	 *   3. 参数值列表 resolvedConstructorArguments 或 preparedConstructorArguments
	 *
	 * 这些信息可用在其他地方,用于进行快捷判断
	 */
	argsHolderToUse.storeCache(mbd, constructorToUse);
	}

继续追踪:

代码语言:javascript
复制
 // ArgumentsHolder.java
        public final Object rawArguments[];

	public final Object arguments[];

	public final Object preparedArguments[];

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;
		}
	}
		}
复制代码

相信大家看到这里应该对resolvedConstructorOrFactoryMethodresolvedConstructorArguments等这几个参数很熟悉。 正如你所想,在前面判断缓存中是否存在的时候,就是通过这几个参数来判断的。

1.3.1.12、实例化Bean对象

strategy.instantiate 这部分代码还是挺多的,我们放在下一章分析。

1.3.2、图解流程

因为这段代码还是挺复杂的,所以我画了一个(explicitArgs=null)的分支流程图,便于理解。

1.3、默认无参构造方法初始化

经过有参构造方法初始化源码的摧残之后,再来看无参的源码,会发现简单多了。

代码语言:javascript
复制
return instantiateBean(beanName, mbd);

继续追踪:

代码语言:javascript
复制
//使用默认的无参构造方法实例化Bean对象
	protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	try {
		Object beanInstance;
		final BeanFactory parent = this;
		//获取系统的安全管理接口,JDK标准的安全管理API
		if (System.getSecurityManager() != null) {
			//这里是一个匿名内置类,根据实例化策略创建实例对象
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					getInstantiationStrategy().instantiate(mbd, beanName, parent),
					getAccessControlContext());
		}
		else {
			//将实例化的对象封装起来
			beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
		}
		BeanWrapper bw = new BeanWrapperImpl(beanInstance);
		initBeanWrapper(bw);
		return bw;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
	}
	}
复制代码

看过有参构造方法初始化的源码之后,再看看无参的,发现代码真的简单太多了,没有复杂的确定构造参数、构造方法的逻辑。

instantiate(mbd, beanName, parent)

代码语言:javascript
复制
//SimpleInstantiationStrategy.java

//使用初始化策略实例化Bean对象
	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
	// Don't override the class with CGLIB if no overrides.
	// 没有覆盖,直接使用反射实例化即可
	if (!bd.hasMethodOverrides()) {
		Constructor<?> constructorToUse;
		synchronized (bd.constructorArgumentLock) {
			//从缓存中获取对象的构造方法或工厂方法
			constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
			//缓存没有
			if (constructorToUse == null) {
				//使用JDK的反射机制,判断要实例化的Bean是否是接口
				final Class<?> clazz = bd.getBeanClass();
				if (clazz.isInterface()) {
					throw new BeanInstantiationException(clazz, "Specified class is an interface");
				}
				try {
					if (System.getSecurityManager() != null) {
						//这里是一个匿名内置类,使用反射机制获取Bean的构造方法
						constructorToUse = AccessController.doPrivileged(
								(PrivilegedExceptionAction<Constructor<?>>) () -> clazz.getDeclaredConstructor());
					}
					else {
						constructorToUse =	clazz.getDeclaredConstructor();
					}
					bd.resolvedConstructorOrFactoryMethod = constructorToUse;
				}
				catch (Throwable ex) {
					throw new BeanInstantiationException(clazz, "No default constructor found", ex);
				}
			}
		}
		//使用BeanUtils实例化,通过反射机制调用”构造方法.newInstance(arg)”来进行实例化
		return BeanUtils.instantiateClass(constructorToUse);
	}
	else {
		// Must generate CGLIB subclass.
		//有方法覆盖,使用CGLIB来实例化对象
		//方法覆盖,在调用目标方法的时候,对调用过程进行拦截,调用实现增强功能的拦截器,返回原来实例的代理
		//所以要用cglib动态代理
		return instantiateWithMethodInjection(bd, beanName, owner);
	}
	}
复制代码

很简单的几个步骤:

  • 判断有无方法覆盖
  • 尝试从缓存中获取构造方法
  • 校验bean是否为interface
  • 利用反射获取默认构造方法
  • 利用BeanUtils实例化

BeanUtils.instantiateClass(constructorToUse)

代码语言:javascript
复制
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
	Assert.notNull(ctor, "Constructor must not be null");
	try {
		// 设置构造方法,可访问
		ReflectionUtils.makeAccessible(ctor);
		// 使用构造方法,创建对象 newInstance
		return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
				KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
	}
	catch (InstantiationException ex) {
		throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
	}
	catch (IllegalAccessException ex) {
		throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
	}
	catch (IllegalArgumentException ex) {
		throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
	}
	catch (InvocationTargetException ex) {
		throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
	}
	}
复制代码

先设置强吻访问,然后newInstance()创建对象。

代码语言:javascript
复制
总结

对于 createBeanInstance() 方法而言,他就是选择合适实例化策略来为 bean 创建实例对象,具体的策略有:

  • Supplier 回调方式
  • 工厂方法初始化
  • 构造函数自动注入初始化
  • 默认构造函数注入。

其中,工厂方法初始化和构造函数自动注入初始化两种方式最为复杂,主要是因为构造函数和构造参数的不确定性,Spring 需要花大量的精力来确定构造函数和构造参数,如果确定了则好办,直接选择实例化策略即可。

当然,在实例化的时候会根据是否有需要覆盖或者动态替换掉的方法,因为存在覆盖或者织入的话需要创建动态代理将方法织入,这个时候就只能选择 CGLIB 的方式来实例化,否则直接利用反射的方式即可,方便快捷。

最后: 到这里实例化Bean的代码就分析完了,这部分源码看起来还是有难度的,看的我头发的慌了,写的也挺累的,但还是会继续写下去,会发现当你研究懂一段源码之后,那种成就感真的很爽0.0

PS: 码字不易,希望大家多多点赞哈,给小弟点动力T.T

参考:公众号-芋道源码

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年09月17日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实例化 Bean
    • 1.1、 Supplier 回调
      • 1.2、工厂方法初始化
        • 1.3、构造函数自动注入初始化
          • 1.3.1、autowireConstructor()
          • 1.3.2、图解流程
        • 1.3、默认无参构造方法初始化
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档