日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)

發布時間:2025/3/15 javascript 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

實例化 Bean

在doCreateBean()代碼 <2> 處,有一行代碼instanceWrapper = createBeanInstance(beanName, mbd, args); 我們追蹤進去看一下:

//AbstractAutowireCapableBeanFactory.java//創建Bean的實例對象protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.//解析beanName 為 classClass<?> 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種方式:

1.Supplier 回調
2.工廠方法初始化
3.構造函數自動注入初始化
4.默認無參構造方法初始化

一、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); }

二、工廠方法初始化

如果存在工廠方法,則使用工廠方法進行初始化。這部分代碼非常長,很復雜,這里就不詳細說了。

三、構造函數自動注入初始化

首先判斷緩存,如果緩存中存在(resolved==true),即已經解析過了,則直接使用已經解析了的。否則,先解析構造函數,然后通過構造函數自動注入初始化。

  • 3.1、autowireConstructor()

autowireConstructor() 這個初始化方法,我們可以簡單理解為通過帶有參數的構造方法,來初始化 Bean 對象。帶有參數的實例化過程相當復雜,因為存在這不確定性,所以在判斷對應參數上做了大量工作。
代碼段如下:

//AbstractAutowireCapableBeanFactory.javapublic 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;//如果構造方法和參數都不為Nullif (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 @ConstructorPropertiesString[] 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;}// 如果兩個構造方法與參數值類型列表之間的差異量一致,那么這兩個方法都可以作為// 候選項,這個時候就出現歧義了,這里先把有歧義的構造方法放入ambiguousConstructorselse 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對象
  • 3.1.1、判斷有無顯式指定構造參數

explicitArgs:外部傳入的指定構造參數
argsToUse:要使用的構造參數
explicitArgs:是指外部傳入的指定構造參數,例如xxxBeanFactory.getBean(“teacher”, “李華”,3),(李華和3)就是傳入的指定參數。
argsToUse 是我們實例化時要使用的構造參數,這里判斷如果explicitArgs不為null的化,就把explicitArgs賦值給 argsToUse。

  • 3.1.2、沒有顯式指定參數,則從緩存中獲取
Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {// 優先嘗試從緩存中獲取,spring對參數的解析過程是比較復雜也耗時的,所以這里先嘗試從緩存中獲取已經解析過的構造函數參數constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;//如果構造方法和參數都不為Nullif (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)

  • 3.1.3、緩存不存在,解析構造函數參數

如果緩存不存在,則需要解析構造函數參數,以確定使用哪一個構造函數來進行實例化

  • 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 構造函數中指定的參數并獲取能解析到的參數個數

  • 3.1.5、獲取所有構造方法

先嘗試獲取指定的構造方法,如果沒有,則利用反射獲取所有構造方法

  • 3.1.6、對所有構造方法排序

排序的主要目的,是為了能夠更加方便的找到最匹配的構造方法,因為構造方法的確認是根據參數個數確認的。排序的規則是:先按照 public / 非 public 構造方法升序,再按照構造參數數量降序。

  • 3.1.7、遍歷所有構造方法

遍歷所有構造方法,篩選出最匹配的一個

  • 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

  • 3.1.9、創建參數持有者 ArgumentsHolder
// 參數持有者 ArgumentsHolder 對象 ArgumentsHolder argsHolder; if (resolvedValues != null) {try {// 獲取注解上的參數名稱 by @ConstructorPropertiesString[] 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) 的。

  • 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;}// 如果兩個構造方法與參數值類型列表之間的差異量一致,那么這兩個方法都可以作為// 候選項,這個時候就出現歧義了,這里先把有歧義的構造方法放入ambiguousConstructorselse 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,我稱之為模棱兩可的構造方法,該集合在《寬松模式》下使用。

至此,所有構造方法都遍歷完畢。如果仍沒有篩選出構造方法,拋出異常。
如果模棱兩可的構造方法不為空,但模式為 嚴格模式,則拋異常。

  • 3.1.11、將解析的構造函數、參數 加入緩存
// 將解析的構造函數、參數 加入緩存 if (explicitArgs == null) {/** 緩存相關信息,比如:* 1. 已解析出的構造方法對象 resolvedConstructorOrFactoryMethod* 2. 構造方法參數列表是否已解析標志 constructorArgumentsResolved* 3. 參數值列表 resolvedConstructorArguments 或 preparedConstructorArguments** 這些信息可用在其他地方,用于進行快捷判斷*/argsHolderToUse.storeCache(mbd, constructorToUse);}

繼續追蹤:

// ArgumentsHolder.javapublic 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;}}}

相信大家看到這里應該對resolvedConstructorOrFactoryMethod 和 resolvedConstructorArguments等這幾個參數很熟悉。
正如你所想,在前面判斷緩存中是否存在的時候,就是通過這幾個參數來判斷的。

  • 3.1.12、實例化Bean對象
    strategy.instantiate 這部分代碼還是挺多的,下回分析。
  • 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標準的安全管理APIif (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對象@Overridepublic 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);}}

很簡單的幾個步驟:

1.判斷有無方法覆蓋
2.嘗試從緩存中獲取構造方法
3.校驗bean是否為interface
4.利用反射獲取默認構造方法
5.利用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);// 使用構造方法,創建對象 newInstancereturn (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 創建實例對象,具體的策略有:

1.Supplier 回調方式2.工廠方法初始化3.構造函數自動注入初始化4.默認構造函數注入。

其中,工廠方法初始化和構造函數自動注入初始化兩種方式最為復雜,主要是因為構造函數和構造參數的不確定性,Spring 需要花大量的精力來確定構造函數和構造參數,如果確定了則好辦,直接選擇實例化策略即可。

當然,在實例化的時候會根據是否有需要覆蓋或者動態替換掉的方法,因為存在覆蓋或者織入的話需要創建動態代理將方法織入,這個時候就只能選擇 CGLIB 的方式來實例化,否則直接利用反射的方式即可,方便快捷。

最后:到這里實例化Bean的代碼就分析完了。

文章轉自:https://cloud.tencent.com/developer/article/1508894

總結

以上是生活随笔為你收集整理的Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。