文档章节

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

大王叫下
 大王叫下
发布于 09/17 20:02
字数 6210
阅读 489
收藏 9

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

简言:
实例化Bean的本质其实就是找到一个合适的构造方法,然后通过构造方法调用newInstance()来实例化Bean。
这样看起来实例化Bean的过程很简单,但其实Spring花费了大量经历去寻找合适的构造方法。

实例化 Bean

doCreateBean()代码 <2> 处,有一行代码instanceWrapper = createBeanInstance(beanName, mbd, args);
这段代码就是实例化Bean的过程。
我们追踪进去看一下:

//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是一个接口,定义如下:

public interface Supplier<T> {

    T get();
    
}

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

// 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 对象。带有参数的实例化过程相当复杂,因为存在这不确定性,所以在判断对应参数上做了大量工作。
代码段如下:

//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、没有显式指定参数,则从缓存中获取
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、获取构造参数个数
        //参数个数
	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、通过参数校验构造方法
// 获取该构造函数的参数类型
	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: 假设现在有一组构造方法按照上面的排序规则进行排序,排序结果如下:

  
     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
        // 参数持有者 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、筛选出符合的构造方法
//通过构造函数参数差异值对比,得出最适合使用的构造函数
		// 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、将解析的构造函数、参数 加入缓存
// 将解析的构造函数、参数 加入缓存
if (explicitArgs == null) {
	/*
	 * 缓存相关信息,比如:
	 *   1. 已解析出的构造方法对象 resolvedConstructorOrFactoryMethod
	 *   2. 构造方法参数列表是否已解析标志 constructorArgumentsResolved
	 *   3. 参数值列表 resolvedConstructorArguments 或 preparedConstructorArguments
	 *
	 * 这些信息可用在其他地方,用于进行快捷判断
	 */
	argsHolderToUse.storeCache(mbd, constructorToUse);
	}

继续追踪:

 // 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

//SimpleInstantiationStrategy.java

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
		final Constructor<?> ctor, @Nullable Object... args) {
	// 没有方法覆盖,直接使用反射实例化即可
	if (!bd.hasMethodOverrides()) {
		if (System.getSecurityManager() != null) {
			// use own privileged to change accessibility (when security is on)
			// 设置构造方法,可访问
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				ReflectionUtils.makeAccessible(ctor);
				return null;
			});
		}
		// 通过 BeanUtils 直接使用构造函数实例化 Bean 对象
		return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));
	}
	else {
		// 使用 CGLIB 创建代理对象
		//方法覆盖,在调用目标方法的时候,对调用过程进行拦截,调用实现增强功能的拦截器,返回原来实例的代理
		//所以要用cglib动态代理
		return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
	}
	}

BeanUtils.instantiateClass(ctor, args)

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

1.3.2、图解流程

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

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

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

return instantiateBean(beanName, mbd);

继续追踪:

//使用默认的无参构造方法实例化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)

//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)

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()创建对象。

总结

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

  • Supplier 回调方式

  • 工厂方法初始化

  • 构造函数自动注入初始化

  • 默认构造函数注入。

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

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

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

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

参考:公众号-芋道源码

© 著作权归作者所有

大王叫下
粉丝 19
博文 65
码字总数 60604
作品 0
杭州
私信 提问
Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机
2018/06/02
0
0
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
2018/05/01
0
0
向Spring大佬低头——大量源码流出解析

用Spring框架做了几年的开发,只停留在会用的阶段上,然而Spring的设计思想和原理确实一个巨大的宝库。大部分人仅仅知道怎么去配,或着加上什么属性就能达到什么效果,这些东西都可以通过查文...

Java团长17
2018/07/11
0
0
【死磕 Spring】—– IOC 之分析各 scope 的 bean 创建

原文出自:http://cmsblogs.com 在 Spring 中存在着不同的 scope,默认是 singleton ,还有 prototype、request 等等其他的 scope,他们的初始化步骤是怎样的呢?这个答案在这篇博客中给出。...

chenssy
2018/10/24
0
0
Spring IOC 容器源码分析系列文章导读

1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了。Spring 包含了众多模块,包括但不限于...

coolblog
2018/05/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
28分钟前
4
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
4
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
13
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
13
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部