Spring源碼解析之推斷構造方法

Spring推斷構造方法

貼個測試代碼直接開幹,這隻是個樣例,其他情況自行分析

@Component
public class OrderService {
 
	public OrderService() {
		System.out.println("無參構造方法");
	}
 
	@Autowired(required = false)
	public OrderService(UserService userService) {
		System.out.println("一個參數的構造方法");
	}
 
	@Autowired(required = false)
	public OrderService(String userName, String passWord) {
		System.out.println("兩個參數的構造方法");
	}
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
 
		// 加載類
		// Make sure bean class is actually resolved at this point.
		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());
		}
 
		// 配置的一種特殊的callback回調方法,通過這個callback創建bean
		// 檢查BeanDefinition是否包含瞭一個Supplier
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			// 如果有就直接調用Supplier的get方法得到一個對象直接返回
			return obtainFromSupplier(instanceSupplier, beanName);
		}
 
		// 通過工廠方法創建
		if (mbd.getFactoryMethodName() != null) {
			// 如果BeanDefinition中存在FactoryMethodName,那麼調用工廠方法得到一個bean對象並返回
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
 
		// 一個類可能有多個構造器,所以Spring得根據參數個數、類型確定需要調用的構造器
		// 在使用構造器創建實例後,Spring會將解析過後確定下來的構造器或工廠方法保存在緩存中,避免再次創建相同bean時再次解析
		// Shortcut when re-creating the same bean...
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					// 已經解析過class的構造器
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			// 已經解析過class的構造器,使用已經解析好的構造器
			if (autowireNecessary) {
				// 如果BeanDefinition中已經構造過
				// 構造函數自動註入
				// 自動裝配構造函數,俗稱推斷構造方法
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				// 使用默認構造器
				return instantiateBean(beanName, mbd);
			}
		}
 
		// TODO 推斷構造方法
		// 需要根據參數解析、確定構造函數
		// 尋找當前實例化的bean中構造器是否有@Autowire註解
		// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
 
		// 解析的構造器不為空 || 註入類型為構造函數自動註入 || beanDefinition指定瞭構造方法參數值 || getBean時指定瞭構造方法參數
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
 
			// 到這裡可能找到瞭多個構造方法,還要決定到底用哪個進行反射初始化
			return autowireConstructor(beanName, mbd, ctors, args);
		}
 
		// 默認構造的首選構造函數?
		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}
 
		// 無需特殊處理:隻需使用默認的無參構造函數
		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors

protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
			throws BeansException {
 
		// 裝配beanPostProcessor的時候會判斷其類型並設置 hasInstantiationAwareBeanPostProcessors 屬性, 符合條件才去找構造函數
		if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
 
			// getBeanPostProcessors拿到beanFactory中的所有BeanPostProcessor接口,找到一個合格的構造函數
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
 
					// 獲取有autowire註解的構造函數 找到合格的構造函數
					// AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors
					Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
					if (ctors != null) {
						return ctors;
					}
				}
			}
		}
		return null;
	}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors

public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
			throws BeanCreationException {
 
		// @Lookup標識的屬性每次調用都會被重新初始化,
		// 有些場景下原型類型的Bean就需要這樣做,否則每個Bean隻會在spring容器初始化的時候創建一次,
		// 但是如果在一個單例的Bean中註入瞭一個原型的Bean,這樣的話原本原型的Bean就相當於變成瞭一個單例的Bean失去瞭原有的意義,
		// 這時就需要@Lookup來解決,或者是每次都從新從spring容器中通過getBean來獲取Bean
		// Let's check for lookup methods here...
		if (!this.lookupMethodsChecked.contains(beanName)) {
			if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
				try {
					Class<?> targetClass = beanClass;
					do {
						ReflectionUtils.doWithLocalMethods(targetClass, method -> {
							Lookup lookup = method.getAnnotation(Lookup.class);
							if (lookup != null) {
								Assert.state(this.beanFactory != null, "No BeanFactory available");
								LookupOverride override = new LookupOverride(method, lookup.value());
								try {
									RootBeanDefinition mbd = (RootBeanDefinition)this.beanFactory.getMergedBeanDefinition(beanName);
									mbd.getMethodOverrides().addOverride(override);
								}
								catch (NoSuchBeanDefinitionException ex) {
									throw new BeanCreationException(beanName,
											"Cannot apply @Lookup to beans without corresponding bean definition");
								}
							}
						});
						targetClass = targetClass.getSuperclass();
					}
					while (targetClass != null && targetClass != Object.class);
 
				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
				}
			}
			this.lookupMethodsChecked.add(beanName);
		}
 
		// 一般隻有原型的bean才會創建多次
		// Quick check on the concurrent map first, with minimal locking.
		Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
		if (candidateConstructors == null) {
			// Fully synchronized resolution now...
			synchronized (this.candidateConstructorsCache) {
				candidateConstructors = this.candidateConstructorsCache.get(beanClass);
				if (candidateConstructors == null) {
					Constructor<?>[] rawCandidates;
					try {
						// 獲取所有構造方法
						rawCandidates = beanClass.getDeclaredConstructors();
					}
					catch (Throwable ex) {
						throw new BeanCreationException(beanName,
								"Resolution of declared constructors on bean Class [" + beanClass.getName() +
								"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
					}
					// 定義要選舉的構造方法集合
					List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
 
					// 加瞭@AutoWire()並且是require=true的構造方法
					Constructor<?> requiredConstructor = null;
 
					// 默認構造發給方法
					Constructor<?> defaultConstructor = null;
 
					// 返回與 Kotlin 主構造函數相對應的 Java 構造函數, 否則,特別是對於非 Kotlin 類,這隻會返回 {@code null}。
					Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
 
					// 記錄合成的構造方法數量,理解為可用的構造方法個數吧
					int nonSyntheticConstructors = 0;
 
					// 遍歷所有的構造方法
					for (Constructor<?> candidate : rawCandidates) {
						if (!candidate.isSynthetic()) {
							nonSyntheticConstructors++;
						}
						else if (primaryConstructor != null) {
							continue;
						}
 
						// 加瞭@Autowired的構造方法
						MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
						if (ann == null) {
							Class<?> userClass = ClassUtils.getUserClass(beanClass);
							if (userClass != beanClass) {
								try {
									Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes());
 
									// 在父類中找@Autowired的構造方法
									ann = findAutowiredAnnotation(superCtor);
								}
								catch (NoSuchMethodException ex) {
									// Simply proceed, no equivalent superclass constructor found...
								}
							}
						}
						if (ann != null) {
							// 如果找到加瞭@Autowired註解的構造方法,再判斷required屬性
 
							// 加瞭@AutoWire()並且是require=true的構造方法
							if (requiredConstructor != null) {
								throw new BeanCreationException(beanName,
										"Invalid autowire-marked constructor: " + candidate +
										". Found constructor with 'required' Autowired annotation already: " +
										requiredConstructor);
							}
							boolean required = determineRequiredStatus(ann);
							if (required) {
								if (!candidates.isEmpty()) {
									throw new BeanCreationException(beanName,
											"Invalid autowire-marked constructors: " + candidates +
											". Found constructor with 'required' Autowired annotation: " +
											candidate);
								}
								requiredConstructor = candidate;
							}
							candidates.add(candidate);
						}
						else if (candidate.getParameterCount() == 0) {
							// 否則如果構造函數參數個數為0,把它賦值給變量defaultConstructor
							defaultConstructor = candidate;
						}
					}
 
					// 處理上面遍歷後的結果
					if (!candidates.isEmpty()) {
						// Add default constructor to list of optional constructors, as fallback.
						if (requiredConstructor == null) {
							// 如果加瞭@Autowired、並且沒有指定required為true、並且存在默認的構造方法
							if (defaultConstructor != null) {
								// 把默認構造方法加到待篩選的集合中
								candidates.add(defaultConstructor);
							}
							else if (candidates.size() == 1 && logger.isInfoEnabled()) {
								logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
										"': single autowire-marked constructor flagged as optional - " +
										"this constructor is effectively required since there is no " +
										"default constructor to fall back to: " + candidates.get(0));
							}
						}
						candidateConstructors = candidates.toArray(new Constructor<?>[0]);
					}
					// 如果隻有一個構造方法,並且構造數的參數大於0
					else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
						candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
					}
					// primaryConstructor 做java開發一般都是null
					else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
							defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
						candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
					}
					// primaryConstructor 做java開發一般都是null
					else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
						candidateConstructors = new Constructor<?>[] {primaryConstructor};
					}
					else {
						candidateConstructors = new Constructor<?>[0];
					}
					// 把推斷的構造方法數組放到緩存map中
					this.candidateConstructorsCache.put(beanClass, candidateConstructors);
				}
			}
		}
		return (candidateConstructors.length > 0 ? candidateConstructors : null);

推斷構造方法第一步,先找出可用的構造方法,步驟如下:

1、先找出所有的構造方法。

2、遍歷所有構造方法,找出加瞭@Autowire的構造方法,如果沒找到就在父類中找,父類中還找不到,但是存在一個構造方法的參數的個數為0,就作為默認的構造方法;如果找到瞭加瞭@Autowire的構造方法,並且require都為true則直接報錯。

3、再次過濾上面篩選過的構造方法,如果有默認構造方法就加入候選者的集合;如果上面篩選過後沒有合適的構造方法,但是又隻有參數個數大於0的構造方法,就把他加入到候選者的列表中。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor

protected BeanWrapper autowireConstructor(
			String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
		// 帶有參數情況的實例化
		return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
	}

org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor

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;
 
		// 1、首先判斷是否通過getBean方法指定瞭構造方法參數值
		// 如果getBean中傳入的參數不為空,那麼就使用傳入的參數
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		// 否則就需要解析配置文件中的參數
		else {
			Object[] argsToResolve = null;
			// 先嘗試從緩存中獲取
			synchronized (mbd.constructorArgumentLock) {
				// 緩存中的構造器
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
 
				// 2、針對當前BeanDefinition是否緩存瞭構造方法和構造方法參數值
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// 在緩存中找到瞭構造器,就繼續從緩存中尋找緩存的構造器參數
					// Found a cached constructor...
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						// 沒有緩存的參數,就需要獲取配置文件中配置的參數
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			// 如果緩存中沒有緩存的參數的話,即argsToResolve不為空,就需要解析配置的參數
			if (argsToResolve != null) {
				// 解析參數類型,比如將配置的String類型轉換成int、boolean等類型
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
			}
		}
 
		// 3、如果兩者任意一個為空,則繼續進行下面的步驟
		// 如果沒有緩存,就需要從構造函數開始解析
		if (constructorToUse == null || argsToUse == null) {
 
			// 如果傳入的構造器數組不為空,就使用傳入的構造器參數,否則通過反射獲取class中定義的構造器
			// Take specified constructors, if any.
			Constructor<?>[] candidates = chosenCtors;
 
			// 3.1 如果沒有傳入構造方法,那麼則獲取當前BeanDefinition對應的BeanClass中所有的構造方法作為候選者
			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);
				}
			}
 
			// 3.2 判斷候選者構造方法是不是隻有一個,並且沒有指定構造方法參數
			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) {
				// getBean方法傳入的參數
				minNrOfArgs = explicitArgs.length;
			}
			else {
				// 配置文件中的配置的參數
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				// 用於承載解析後的構造函數參數的值
				resolvedValues = new ConstructorArgumentValues();
				// 解析配置文件中的參數,並且返回參數個數
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}
 
			// 3.3 對候選者構造函數排序,public構造函數優先、參數數量降序排序
			AutowireUtils.sortConstructors(candidates);
 
			// 計算構造方法參數個數最少個數
			// 意思是如果指定瞭構造方法參數個數,所以當前BeanDefinition對應的BeanClass中所有構造方法參數個數至少滿足手動指定的參數值個數
			int minTypeDiffWeight = Integer.MAX_VALUE;
 
			Set<Constructor<?>> ambiguousConstructors = null;
			LinkedList<UnsatisfiedDependencyException> causes = null;
 
			// 3.4 遍歷所有的構造方法
			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;
				}
 
				// 如果候選者參數個數 < minNrOfArgs,則不匹配,繼續下一個
				if (parameterCount < minNrOfArgs) {
					continue;
				}
 
				// 封裝解析到的參數信息
				ArgumentsHolder argsHolder;
 
				Class<?>[] paramTypes = candidate.getParameterTypes();
 
				// 解析配置文件得到的構造方法參數值
				if (resolvedValues != null) {
					try {
						// 3.5 判斷通過getBean方法指定構造方法參數
						String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
						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, 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 {
					// 處理參數由getBean方法傳入的情況
					// Explicit arguments given -> arguments length must match exactly.
					if (parameterCount != explicitArgs.length) {
						continue;
					}
					argsHolder = new ArgumentsHolder(explicitArgs);
				}
 
				// 3.7 計算得到的構造方法參數值和參數的匹配程度
				// 因為不同構造函數的參數個數相同,而且參數類型為父子關系,所以需要找出類型最符合的一個構造函數
				// Spring用一種權重的形式來表示類型差異程度,差異權重越小越優先
				// 如果是以寬松的方式解析,默認為true,所以執行getTypeDifferenceWeight
				int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
						argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
 
				// 當前構造函數最為匹配的話,清空先前ambiguousConstructors列表
				// Choose this constructor if it represents the closest match.
				if (typeDiffWeight < minTypeDiffWeight) {
					constructorToUse = candidate;
					argsHolderToUse = argsHolder;
					argsToUse = argsHolder.arguments;
					minTypeDiffWeight = typeDiffWeight;
					ambiguousConstructors = null;
				}
				// 存在相同權重的構造器,將構造器添加到一個ambiguousConstructors列表變量中
				// 註意,這時候constructorToUse 指向的仍是第一個匹配的構造函數
				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()) {
				// 如果存在多個構造函數匹配程度相同,並且BeanDefinition中設置isLenientConstructorResolution為false(默認值為true),
				// 表示構造器創建為嚴格模式的話,會拋出異常。異常代碼略
				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");
 
		// 初始化
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}

1、隻有一個無參的構造方法,那麼直接使用無參的構造方法進行實例化 candidates.length == 1

2、有多個構造方法或者bean需要通過構造方法自動進行註入 ResolvedAutowireMode() == 3

3、根據所指定的構造方法參數值,確定所需要的最少的構造方法參數值的個數 minNrOfArgs

4、對所有的構造方法進行排序,參數個數多的在前面 AutowireUtils.sortConstructors

5、遍歷所有的構造方法

6、如果當前構造方法參數個數小於minNrOfArgs則不匹配,繼續判斷下一個構造方法

7、如果是調用getBean方法指定的參數就直接利用這些值,如果不是就根據構造方法參數類型找值(先byType再byName),匹配的話則封裝成一個ArgumentsHolder對象

8、這裡可能會匹配到多個構造方法,然後就需要那值和構造方法匹配程度計算一個權重,值越小優先級越高(因為如果是父子類的話子類匹配成功更高)

9、計算權重分為寬松型(默認)和嚴格型,嚴格型的情況下如果有多個匹配就報錯

到此這篇關於Spring源碼解析之推斷構造方法的文章就介紹到這瞭,更多相關Spring推斷構造方法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: