Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)

news2024/10/23 2:48:49

序言

前面文章介绍了在Spring中多种创建Bean实例的方式,包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。
这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多,感兴趣可以当扩展了解。

doCreateBean

同样是doCreateBean下的createBeanInstance()

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		//这个beanWrapper是用来持有创建出来的bean对象的
		BeanWrapper instanceWrapper = null;

		//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息
		if (mbd.isSingleton()) {
			// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 没有就创建实例
		if (instanceWrapper == null) {
			// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// 去除无用代码..... 
	}

createBeanInstance
我们在上篇中介绍了通过Supplier创建Bean的方式,而代码中Supplier判断的下面,就是我们本篇文章要介绍的通过factoryMethod方式创建Bean。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 锁定class,根据设置的class属性或者根据className来解析class
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		// 如果beanClass != null
		// 并且,访问修饰符不是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());
		}
		// 获取定义的Supplier
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		return instantiateBean(beanName, mbd);
	}

测试类
我们这里准备两个测试类,一个是静态工厂的方式,一个是普通的方式进行Bean实例的创建。

PersonStaticFactory
通过静态方法创建Person对象。

public class PersonStaticFactory {
	public static Person getPerson(String name)
	{
		Person person = new Person();
		person.setName(name);
		person.setAge(22);
		return person;
	}
}

PersonInstanceFactory
普通方法创建Bean实例。

public class PersonInstanceFactory {

	public Person getPerson(String name){
		Person person = new Person();
		person.setAge(18);
		person.setName(name);
		return person;
	}
}

Person
平平无奇Person对象。

public class Person {

	private String name;
	private int age;
	
	// 省略 get  set toString和构造函数
}

factoryMethod.xml

<?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="personInstanceFactory" class="org.springframework.factoryMethod.PersonInstanceFactory">
	</bean>

	<bean id="person" class="org.springframework.factoryMethod.PersonStaticFactory" factory-method="getPerson">
		<constructor-arg name="name" value="zhangsan"/>
	</bean>

	<bean id="person2" class="org.springframework.factoryMethod.Person"
		  factory-bean="personInstanceFactory" factory-method="getPerson">
		<constructor-arg name="name" value="wangwu"></constructor-arg>
	</bean>
</beans>

main
测试main方法,让我们看看在getBean()中都做了什么操作。

public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("factoryMethod.xml");
		Person person1 =(Person) ac.getBean("person");
		Person person2 =(Person)ac.getBean("person2");
		System.out.println(person1);
		System.out.println(person2);
}

instantiateUsingFactoryMethod()

方法入口。我们这里以 person2 的创建为例。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 锁定class,根据设置的class属性或者根据className来解析class
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		// 如果beanClass != null
		// 并且,访问修饰符不是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());
		}
		// 获取定义的Supplier
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		return instantiateBean(beanName, mbd);
	}

让我们顺着Supplier的判断顺着往下看。
此时来到了person对象的加载,根据xml文件的配置,此时的beanName所指的person2是Person对象
在这里插入图片描述

创建构造器解析器对象,调用instantiateUsingFactoryMethod()方法进行解析。

protected BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
			
		return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

instantiateUsingFactoryMethod

源码很长,我们这块分段来看。
代码中的第一部分主要是判断当前的BeanDefinition是否包含factoryBeanName,并以此来区分是否是static修饰,并设置标志位。

public BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

		// 创建 BeanWrapperImpl 对象
		BeanWrapperImpl bw = new BeanWrapperImpl();
		//初始化beanWrapper对象,这里面获取了所有的customerEditor进行注册
		this.beanFactory.initBeanWrapper(bw);

		Object factoryBean;
		Class<?> factoryClass;
		boolean isStatic;
		//获取factoryBeanName
		String factoryBeanName = mbd.getFactoryBeanName();

		//如果factoryBeanName不为null
		if (factoryBeanName != null) {
			//如果配置的 factoryBeanName与beanName名称相同,则抛出异常
			if (factoryBeanName.equals(beanName)) {
				throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
						"factory-bean reference points back to the same bean definition");
			}
			//从工厂中获取当前factoryBeanName对应的bean对象
			factoryBean = this.beanFactory.getBean(factoryBeanName);
			//如果是单例模式,并且容器中已经存在该bean对象,则抛出异常
			if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
				//意味着此时工厂中已经包含了beanName对应的实例对象,再生成则重复了。,抛出异常
				throw new ImplicitlyAppearedSingletonException();
			}
			// 获取factoryBean的Class对象
			factoryClass = factoryBean.getClass();
			// 标志位设置为false 表示不是静态方法。
			isStatic = false;
		}
		else {
			// It's a static factory method on the bean class.
			// 这是bean类上的静态工厂方法
			// 如果mbd没有指定bean类
			if (!mbd.hasBeanClass()) {
				throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
						"bean definition declares neither a bean class nor a factory-bean reference");
			}
			factoryBean = null;
			factoryClass = mbd.getBeanClass();
			// 标志位设置为 true 表示为静态方法。
			isStatic = true;
		}
	}	

我们这里的person2是普通工厂,并且会在代码this.beanFactory.getBean(factoryBeanName);中通过factoryBeanName来创建我们所配置的PersonInstanceFactory对象。
在这里插入图片描述

声明变量并看调用getBean()方法时是否传了显示的参数。我们这里调用getBean时,什么都没传。所以expliciArgs为null。

		//声明一个要使用的工厂方法,默认为null
		Method factoryMethodToUse = null;
		//声明一个用于存储不同形式的参数值的ArgumentsHolder,默认为null
		ArgumentsHolder argsHolderToUse = null;
		// 声明一个要使用的参数值数组,默认为null
		Object[] argsToUse = null;

		// 如果调用getBean方法时,显示的传了参数,则此时explicitArgs != null
		// 则argsToUser引用explicitArgs
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			//如果没有传
			// 声明一个要解析的args[] , 默认为null。
			Object[] argsToResolve = null;
			synchronized (mbd.constructorArgumentLock) {
				// factoryMethodToUse引用mbd中已经解析构造器或工厂方法对象
				factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
				//如果此时factoryMethodToUse不为null,并且mbd已解析构造函数参数(默认为false)
				if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached factory method...
					//找到了缓存工厂方法
					//argsToUse引用完全解析构的造器函数参数
					argsToUse = mbd.resolvedConstructorArguments;
					//如果依然为null
					if (argsToUse == null) {
						//引用mbd准备好的构造函数参数值
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			//如果argsToResolve不为null
			if (argsToResolve != null) {
				//解析mbd中缓存好的参数值
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
			}
		}

获取所有候选方法并进行过滤。

// 如果没解析过,就获取factoryClass的用户定义类型,因为此时factoryClass可能是CGLIB动态代理类型,
		// 所以要获取用父类的类型。如果工厂方法是唯一的,就是没重载的,就获取解析的工厂方法,如果不为空,就添加到一个不可变列表里,
		// 如果为空的话,就要去找出factoryClass的以及父类的所有的方法,进一步找出方法修饰符一致且名字跟工厂方法名字相同的且是bean注解的方法,并放入列表里。
		if (factoryMethodToUse == null || argsToUse == null) {
			// Need to determine the factory method...
			// Try all methods with this name to see if they match the given arguments.
			// 获取工厂类的所有候选工厂方法
			factoryClass = ClassUtils.getUserClass(factoryClass);

			// 定义一个用于存储候选方法的集合
			List<Method> candidates = null;
			// 如果mbd所配置工厂方法时唯一
			if (mbd.isFactoryMethodUnique) {
				// 如果factoryMethodToUse为null
				if (factoryMethodToUse == null) {
					// 获取mbd解析后的工厂方法对象
					factoryMethodToUse = mbd.getResolvedFactoryMethod();
				}
				// 如果factoryMethodToUse不为null
				if (factoryMethodToUse != null) {
					// Collections.singletonList()返回的是不可变的集合,但是这个长度的集合只有1,可以减少内存空间。但是返回的值依然是Collections的内部实现类,
					// 同样没有add的方法,调用add,set方法会报错
					// 新建一个不可变,只能存一个对象的集合,将factoryMethodToUse添加进行,然后让candidateList引用该集合
					candidates = Collections.singletonList(factoryMethodToUse);
				}
			}
			// 如果candidateList为null
			if (candidates == null) {
				// 让candidateList引用一个新的ArrayList
				candidates = new ArrayList<>();
				// 根据mbd的是否允许访问非公共构造函数和方法标记【RootBeanDefinition.isNonPublicAccessAllowed】来获取factoryClass的所有候选方法
				Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
				// 遍历rawCandidates,元素名为candidate
				for (Method candidate : rawCandidates) {
					// 如果candidate的修饰符与isStatic一致且candidate有资格作为mdb的工厂方法
					if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
						// 将candidate添加到candidateList中
						candidates.add(candidate);
					}
				}
			}

			// 候选方法只有一个且没有构造函数时,就直接使用该候选方法生成与beanName对应的Bean对象封装到bw中返回出去
			// 如果candidateList只有一个元素且没有传入构造函数值且mbd也没有构造函数参数值
			if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
				// 获取candidateList中唯一的方法
				Method uniqueCandidate = candidates.get(0);
				// 如果uniqueCandidate是不需要参数
				if (uniqueCandidate.getParameterCount() == 0) {
					// 让mbd缓存uniqueCandidate【{@link RootBeanDefinition#factoryMethodToIntrospect}】
					mbd.factoryMethodToIntrospect = uniqueCandidate;
					// 使用mdb的构造函数字段的通用锁【{@link RootBeanDefinition#constructorArgumentLock}】进行加锁以保证线程安全
					synchronized (mbd.constructorArgumentLock) {
						// 让mbd缓存已解析的构造函数或工厂方法【{@link RootBeanDefinition#resolvedConstructorOrFactoryMethod}】
						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
						// 让mbd标记构造函数参数已解析【{@link RootBeanDefinition#constructorArgumentsResolved}】
						mbd.constructorArgumentsResolved = true;
						// 让mbd缓存完全解析的构造函数参数【{@link RootBeanDefinition#resolvedConstructorArguments}】
						mbd.resolvedConstructorArguments = EMPTY_ARGS;
					}
					// 使用factoryBean生成的与beanName对应的Bean对象,并将该Bean对象保存到bw中
					bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
					// 将bw返回出去
					return bw;
				}
			}

			// 如果有多个工厂方法的话进行排序操作
			if (candidates.size() > 1) {  // explicitly skip immutable singletonList
				candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
			}

因为所有的对象的父类都是Object,所以会获取很多Object中的方法,经过过滤后,这里candidates只保留了getPerson()
在这里插入图片描述
过滤后,只剩getPerson()方法。
在这里插入图片描述
根据candidate数量,如果只有一个且符合条件,则进行对象的创建,如果有多个则进行排序。
如果有构造函数,则获取构造函数的属性和值。

			// 候选方法只有一个且没有构造函数时,就直接使用该候选方法生成与beanName对应的Bean对象封装到bw中返回出去
			// 如果candidateList只有一个元素且没有传入构造函数值且mbd也没有构造函数参数值
			if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
				// 获取candidateList中唯一的方法
				Method uniqueCandidate = candidates.get(0);
				// 如果uniqueCandidate是不需要参数
				if (uniqueCandidate.getParameterCount() == 0) {
					// 让mbd缓存uniqueCandidate【{@link RootBeanDefinition#factoryMethodToIntrospect}】
					mbd.factoryMethodToIntrospect = uniqueCandidate;
					// 使用mdb的构造函数字段的通用锁【{@link RootBeanDefinition#constructorArgumentLock}】进行加锁以保证线程安全
					synchronized (mbd.constructorArgumentLock) {
						// 让mbd缓存已解析的构造函数或工厂方法【{@link RootBeanDefinition#resolvedConstructorOrFactoryMethod}】
						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
						// 让mbd标记构造函数参数已解析【{@link RootBeanDefinition#constructorArgumentsResolved}】
						mbd.constructorArgumentsResolved = true;
						// 让mbd缓存完全解析的构造函数参数【{@link RootBeanDefinition#resolvedConstructorArguments}】
						mbd.resolvedConstructorArguments = EMPTY_ARGS;
					}
					// 使用factoryBean生成的与beanName对应的Bean对象,并将该Bean对象保存到bw中
					bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
					// 将bw返回出去
					return bw;
				}
			}

			// 如果有多个工厂方法的话进行排序操作
			if (candidates.size() > 1) {  // explicitly skip immutable singletonList
				candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
			}

			// ConstructorArgumentValues:构造函数参数值的Holder,通常作为BeanDefinition的一部分,支持构造函数参数列表中特定索引的值
			// 以及按类型的通用参数匹配
			// 定义一个用于存放解析后的构造函数参数值的ConstructorArgumentValues对象
			ConstructorArgumentValues resolvedValues = null;
			// 定义一个mbd是否支持使用构造函数进行自动注入的标记
			boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
			// 定义一个最小类型差异权重,默认是Integer最大值
			int minTypeDiffWeight = Integer.MAX_VALUE;
			// 定义一个存储摸棱两可的工厂方法的Set集合,以用于抛出BeanCreationException时描述异常信息
			Set<Method> ambiguousFactoryMethods = null;

			// 定义一个最少参数数,默认为0
			int minNrOfArgs;
			// 如果explicitArgs不为null
			if (explicitArgs != null) {
				// minNrOfArgs引用explicitArgs的数组长度
				minNrOfArgs = explicitArgs.length;
			}
			else {
				// We don't have arguments passed in programmatically, so we need to resolve the
				// arguments specified in the constructor arguments held in the bean definition.
				// 我们没有以编程方式传递参数,因此我们需要解析BeanDefinition中保存的构造函数参数中指定的参数
				// 如果mbd有构造函数参数值
				if (mbd.hasConstructorArgumentValues()) {
					// 获取mbd的构造函数参数值Holder
					ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
					// 对resolvedValues实例化
					resolvedValues = new ConstructorArgumentValues();
					// 将cargs解析后值保存到resolveValues中,并让minNrOfArgs引用解析后的最小(索引参数值数+泛型参数值数)
					minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
				}
				else {
					// 意味着mbd没有构造函数参数值时,将minNrOfArgs设为0
					minNrOfArgs = 0;
				}
			}

获取构造函数的属性和值。
在这里插入图片描述
获取参数列表并进行封装。

	// 定义一个用于UnsatisfiedDependencyException的列表
			LinkedList<UnsatisfiedDependencyException> causes = null;

			// 遍历candidates,元素名为candidate
			for (Method candidate : candidates) {
				// 获取参数的个数
				int parameterCount = candidate.getParameterCount();

				// 如果paramTypes的数组长度大于等于minNrOfArgs
				if (parameterCount >= minNrOfArgs) {
					// ArgumentsHolder:用于保存参数数组
					// 定义一个封装参数数组的ArgumentsHolder对象
					ArgumentsHolder argsHolder;
					// 获取candidate的参数类型数组
					Class<?>[] paramTypes = candidate.getParameterTypes();
					// 如果explicitArgs不为null
					if (explicitArgs != null) {
						// Explicit arguments given -> arguments length must match exactly.
						// 给定的显示参数->参数长度必须完全匹配
						// 如果paramTypes的长度与explicitArgsd额长度不相等
						if (paramTypes.length != explicitArgs.length) {
							// 跳过当次循环中剩下的步骤,执行下一次循环。
							continue;
						}
						// 实例化argsHolder,封装explicitArgs到argsHolder
						argsHolder = new ArgumentsHolder(explicitArgs);
					}
					else {
						// Resolved constructor arguments: type conversion and/or autowiring necessary.
						// 已解析的构造函数参数:类型转换 and/or 自动注入时必须的
						try {
							// 定义用于保存参数名的数组
							String[] paramNames = null;
							// 获取beanFactory的参数名发现器
							ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
							// 如果pnd不为null
							if (pnd != null) {
								// 通过pnd解析candidate的参数名
								paramNames = pnd.getParameterNames(candidate);
							}
							// 将resolvedValues转换成一个封装着参数数组ArgumentsHolder实例,当candidate只有一个时,支持可在抛
							// 出没有此类BeanDefinition的异常返回null,而不抛出异常
							argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
									paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
						}
						// 捕捉UnsatisfiedDependencyException
						catch (UnsatisfiedDependencyException ex) {
							if (logger.isTraceEnabled()) {
								logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
							}
							// Swallow and try next overloaded factory method.
							// 吞下并尝试下一个重载的工厂方法
							// 如果cause为null
							if (causes == null) {
								// 对cause进行实例化成LinkedList对象
								causes = new LinkedList<>();
							}
							// 将ex添加到causes中
							causes.add(ex);
							// 跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环
							continue;
						}
					}

Spring会计算权重,选出符合条件的方法来进行实例对象的创建。

				// mbd支持的构造函数解析模式,默认使用宽松模式:
					// 1. 严格模式如果摸棱两可的构造函数在转换参数时都匹配,则抛出异常
					// 2. 宽松模式将使用"最接近类型匹配"的构造函数
					// 如果bd支持的构造函数解析模式时宽松模式,引用获取类型差异权重值,否则引用获取Assignabliity权重值
					int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
							argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
					// Choose this factory method if it represents the closest match.
					// 如果它表示最接近的匹配项,则选择此工厂方法
					// 如果typeDiffWeight小于minTypeDiffWeight
					if (typeDiffWeight < minTypeDiffWeight) {
						// 让factoryMethodToUser引用candidate
						factoryMethodToUse = candidate;
						// 让argsHolderToUse引用argsHolder
						argsHolderToUse = argsHolder;
						// 让argToUse引用argsHolder的经过转换后参数值数组
						argsToUse = argsHolder.arguments;
						// 让minTypeDiffWeight引用typeDiffWeight
						minTypeDiffWeight = typeDiffWeight;
						// 将ambiguousFactoryMethods置为null
						ambiguousFactoryMethods = null;
					}
					// Find out about ambiguity: In case of the same type difference weight
					// for methods with the same number of parameters, collect such candidates
					// and eventually raise an ambiguity exception.
					// However, only perform that check in non-lenient constructor resolution mode,
					// and explicitly ignore overridden methods (with the same parameter signature).
					// 找出歧义:如果具有相同数量参数的方法具有相同的类型差异权重,则收集此类候选想并最终引发歧义异常。
					// 但是,仅在非宽松构造函数解析模式下执行该检查,并显示忽略的方法(具有相同的参数签名)
					// 如果factoryMethodToUse不为null且typeDiffWeight与minTypeDiffWeight相等
					// 且mbd指定了严格模式解析构造函数且paramTypes的数组长度与factoryMethodToUse的参数数组长度相等且
					// paramTypes的数组元素与factoryMethodToUse的参数数组元素不相等
					else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
							!mbd.isLenientConstructorResolution() &&
							paramTypes.length == factoryMethodToUse.getParameterCount() &&
							!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
						// 如果ambiguousFactoryMethods为null
						if (ambiguousFactoryMethods == null) {
							// 初始化ambiguousFactoryMethods为LinkedHashSet实例
							ambiguousFactoryMethods = new LinkedHashSet<>();
							// 将factoryMethodToUse添加到ambiguousFactoryMethods中
							ambiguousFactoryMethods.add(factoryMethodToUse);
						}
						// 将candidate添加到ambiguousFactoryMethods中
						ambiguousFactoryMethods.add(candidate);
					}
				}
			}

			// 整合无法筛选出候选方法或者无法解析出要使用的参数值的情况,抛出BeanCreationException并加以描述
			// 如果factoryMethodToUse为null或者argsToUse为null
			if (factoryMethodToUse == null || argsToUse == null) {
				// 如果causes不为null
				if (causes != null) {
					// 从cause中移除最新的UnsatisfiedDependencyException
					UnsatisfiedDependencyException ex = causes.removeLast();
					// 遍历causes,元素为cause
					for (Exception cause : causes) {
						// 将cause添加到该Bean工厂的抑制异常列表【{@link DefaultSingletonBeanRegistry#suppressedExceptions】中
						this.beanFactory.onSuppressedException(cause);
					}
					// 重新抛出ex
					throw ex;
				}
				// 定义一个用于存放参数类型的简单类名的ArrayList对象,长度为minNrOfArgs
				List<String> argTypes = new ArrayList<>(minNrOfArgs);
				// 如果explicitArgs不为null
				if (explicitArgs != null) {
					// 遍历explicitArgs.元素为arg
					for (Object arg : explicitArgs) {
						// 如果arg不为null,将arg的简单类名添加到argTypes中;否则将"null"添加到argTypes中
						argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
					}
				}
				// 如果resolvedValues不为null
				else if (resolvedValues != null) {
					// 定义一个用于存放resolvedValues的泛型参数值和方法参数值的LinkedHashSet对象
					Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
					// 将resolvedValues的方法参数值添加到valueHolders中
					valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
					// 将resolvedValues的泛型参数值添加到valueHolders中
					valueHolders.addAll(resolvedValues.getGenericArgumentValues());
					// 遍历valueHolders,元素为value
					for (ValueHolder value : valueHolders) {
						// 如果value的参数类型不为null,就获取该参数类型的简单类名;否则(如果value的参数值不为null,即获取该参数值的简单类名;否则为"null")
						String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :
								(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));
						// 将argType添加到argTypes中
						argTypes.add(argType);
					}
				}
				// 将argType转换成字符串,以","隔开元素.用于描述Bean创建异常
				String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
				// 抛出BeanCreationException:找不到匹配的工厂方法:工厂Bean'mbd.getFactoryBeanName()';工厂方法
				// 'mbd.getFactoryMethodName()(argDesc)'.检查是否存在具体指定名称和参数的方法,并且该方法时静态/非静态的.
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"No matching factory method found: " +
						(mbd.getFactoryBeanName() != null ?
							"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
						"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
						"Check that a method with the specified name " +
						(minNrOfArgs > 0 ? "and arguments " : "") +
						"exists and that it is " +
						(isStatic ? "static" : "non-static") + ".");
			}
			// 如果factoryMethodToUse时无返回值方法
			else if (void.class == factoryMethodToUse.getReturnType()) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Invalid factory method '" + mbd.getFactoryMethodName() +
						"': needs to have a non-void return type!");
			}
			// 如果ambiguousFactoryMethods不为null
			else if (ambiguousFactoryMethods != null) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Ambiguous factory method matches found in bean '" + beanName + "' " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
						ambiguousFactoryMethods);
			}

			// 将筛选出来的工厂方法和解析出来的参数值缓存到mdb中
			// 如果explicitArgs为null且argsHolderToUser不为null
			if (explicitArgs == null && argsHolderToUse != null) {
				// 让mbd的唯一方法候选【{@link RootBeanDefinition#factoryMethodToIntrospect}】引用factoryMethodToUse
				mbd.factoryMethodToIntrospect = factoryMethodToUse;
				// 将argsHolderToUse所得到的参数值属性缓存到mbd对应的属性中
				argsHolderToUse.storeCache(mbd, factoryMethodToUse);
			}
		}

		// 使用factoryBean生成与beanName对应的Bean对象,并将该Bean对象保存到bw中
		bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
		// 将bw返回出去
		return bw;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1843694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子书(chm)-加载JS--CS上线

免责声明: 本文仅做技术交流与学习... 目录 cs--web投递 html(js)代码 html生成chm工具--EasyCHM 1-选择powershell 模式 生成 2-选择bitsadmin模式生成 chm反编译成html cs--web投递 cs配置监听器--->攻击---->web投递---> 端口选择没占用的, URL路径到时候会在…

HALCON-从入门到入门-霍夫识别直线

1.废话 霍夫变换是一种特征检测&#xff0c;被广泛应用在图像分析、计算机视觉以及数位影像处理。霍夫变换是用来辨别找出物件中的特征&#xff0c;例如&#xff1a;线条。他的算法流程大致如下&#xff0c;给定一个物件、要辨别的形状的种类&#xff0c;算法会在参数空间中执…

【JavaEE】Spring Boot MyBatis详解(二)

一.解决数据库字段名和对象属性名冲突的问题. 产生这个问题的本质原因就是Java 属性名和数据库字段的命名规范不同. 这个问题的本质就是查询数据库返回了字段,但是不知道和Java对象的哪个属性相对应 1.注解的解决方法 注解的解决方式有三种: 方式一:给数据库字段起别名. 本质…

Zookeeper 一、Zookeeper简介

1.分布式系统定义及面临的问题 分布式系统是同时跨越多给物理主机&#xff0c;独立运行的多个软件所组成的系统。类比一下&#xff0c;分布式系统就是一群人一起干活。人多力量大&#xff0c;每个服务器的算力是有限的&#xff0c;但是通过分布式系统&#xff0c;由n个服务器组…

Linux环境如何彻底卸载感干净RabbitMQ并重新安装

Linux&#xff08;Centos7&#xff09;环境如何彻底卸载感干净RabbitMQ并重新安装 我这个是超级简单的&#xff0c;如果安装不好&#xff0c;顺着网线来找我 一、卸载RabbitMq相关的软件包 1. 先停止RabbitMq服务 systemctl stop rabbitmq-server2. 查看rabbitmq安装的相关…

Microsoft Edge浏览器安装crx拓展插件教程

1、首先打开edge浏览器&#xff0c;点击顶部地址栏。 2、在地址栏中输入"edge://flags/#extensions-on-edge-urls"并按下回车。2、在地址栏中输入"edge://flags/#extensions-on-edge-urls"并按下回车。 3、进入后&#xff0c;将图示选项改为“已禁用”。 …

Redis缓存的一些概念性问题

目录 缓存模型和思路 缓存更新策略 数据库和缓存不一致 缓存与数据库双写一致 缓存穿透 缓存雪崩 缓存击穿 速度快,好用&#xff0c;内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力 缓存模型和思路 标准的操作方式就是查询数据库之前先…

多线程下JVM内存模型 和 volatile关键字

1、线程的概念 线程&#xff08;thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线程并行执行不同的任务…

从复用性角度阐述中台建设

目录 复用性中台定义深思中台建设产品线形态何时演变中台能力落地中台 业务中台架构总结 技术学习永不止步&#xff0c;最近也是看了很多关于架构设计相关的专栏&#xff0c;慢慢总结出来一部分知识&#xff0c;代入自己的思考与理解&#xff0c;以及结合并反思自己之前公司的架…

开源!在goview中实现cesium的低代码可视化编辑

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀&#xff0c;这是2024年输出的第19/100篇文章&#xff1b; 前言 前阵子写了一篇goview二开的文章教程&#xff0c;很多小伙伴留言对goview嵌套cesium并实现…

【Python日志模块全面指南】:记录每一行代码的呼吸,掌握应用程序的脉搏

文章目录 &#x1f680;一、了解日志&#x1f308;二、日志作用&#x1f308;三、了解日志模块⭐四、日志级别&#x1f4a5;五、记录日志-基础❤️六、记录日志-处理器handler&#x1f3ac;七、记录日志-格式化记录☔八、记录日志-配置logger&#x1f44a;九、流程梳理 &#x…

基于Windows API DialogBox的对话框

在C中&#xff0c;DialogBox函数是Windows API的一部分&#xff0c;它用于在Win32应用程序中创建并显示一个模态对话框。DialogBox函数是USER32.DLL中的一个导出函数&#xff0c;因此你需要在你的C Win32应用程序中链接到这个库。 #include "framework.h" #include …

劲爆!Kimi月之暗面可以接入微信,智能升级, 打造个性多Agent(二)

前言 在当今这个快速发展的AI时代&#xff0c;抖音推出了一个名为“扣子Coze”的工具&#xff0c;帮助用户快速、低门槛地搭建属于自己的AI机器人。本文将详细介绍如何使用扣子Coze配置自己的AI Agent&#xff0c;并展示其在多个平台上的应用。 如何使用多个Agent 搭建更加智…

【可控图像生成系列论文(二)】MimicBrush 港大、阿里、蚂蚁集团合作论文解读2

【可控图像生成系列论文&#xff08;一&#xff09;】简要介绍了论文的整体流程和方法&#xff0c;本文则将就整体方法、模型结构、训练数据和纹理迁移进行详细介绍。 1.整体方法 MimicBrush 的整体框架如下图所示。为了实现模仿编辑&#xff0c;作者设计了一种具有双扩散模型…

2024年6月20日 (周四) 叶子游戏新闻

超市播音系统: 定时播放不同音乐 强制卸载软件: 一款强制卸载软件 免费多人沙盒游戏《宝藏世界》推出更新“潮起潮落”&#xff0c;带来全新克苏鲁风冒险准备好迎接一场超凡的冒险吧&#xff0c;MMORPG发行商gamigo宣布《宝藏世界》的最新更新&#xff1a;“潮起潮落”。这次更…

我的创作纪念日--码农阿豪

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

C语言程序设计-7 数组

在程序设计中&#xff0c;为了处理方便&#xff0c;把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在&#xff23;语言中&#xff0c;数组属于构造数据类型。一个数 组可以分解为多个数组元素&#xff0c;这些数组元素可以是基本数…

性能测试-性能监控分析与调优(三)《实战》

性能监控 使用命令监控 cpu瓶颈分析 top命令 在进行性能测试时使用top命令&#xff0c;界面如下 上图可以看出 CPU 概况区&#xff1a; %Cpu(s): us&#xff08;用户进程占用CPU的百分比&#xff09;, 和 sy&#xff08;系统进程占用CPU的百分比&#xff09; 的数值很高…

Mybatis Plus 详解 IService、BaseMapper、自动填充、分页查询功能

结构直接看目录 前言 MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 愿景 我们的愿景是成为 MyBatis 最好的搭档&#xff0c;就像 魂斗罗 中的 1P、2P&#xff0c;基友搭配&#xff0c;效…

【前端技巧】css篇

利用counter实现计数器 counter-reset&#xff1a;为计数器设置名称&#xff0c;语法如下&#xff1a; counter-rese: <idntifier><integer>第一个参数为变量名称&#xff0c;第二个参数为初始值&#xff0c;默认为0 counter-increment&#xff1a;设置计数器增…