一文詳解Spring構(gòu)造函數(shù)推斷
正文
Spring 提供了一組基本的功能,例如依賴注入(DI)和面向切面編程(AOP)。其中一個(gè)非常強(qiáng)大的功能是構(gòu)造函數(shù)自動(dòng)注入,也稱為構(gòu)造函數(shù)推斷。在本文中,我們將深入探討Spring構(gòu)造函數(shù)推斷的底層原理,并解釋Spring是如何實(shí)現(xiàn)它的。
自動(dòng)注入
構(gòu)造函數(shù)自動(dòng)注入是指 Spring 自動(dòng)解析 bean 的構(gòu)造函數(shù)參數(shù),并將它們傳遞給相應(yīng)的構(gòu)造函數(shù)。這樣,我們就不必顯式地在XML或Java配置文件中指定每個(gè) bean 的構(gòu)造函數(shù)參數(shù)。這是一個(gè)非常方便的功能,特別是在有許多 bean 需要注入的情況下。
底層原理
Spring 使用 Java 反射機(jī)制來(lái)執(zhí)行構(gòu)造函數(shù)推斷。當(dāng) Spring 需要實(shí)例化一個(gè) bean 時(shí),它將首先使用 Java 反射機(jī)制檢查該 bean 的構(gòu)造函數(shù)。
然后,它將檢查每個(gè)構(gòu)造函數(shù)的參數(shù)類(lèi)型,并嘗試在 Spring 應(yīng)用程序上下文中查找與參數(shù)類(lèi)型匹配的 bean。
如果找到匹配的 bean,則 Spring 將使用該 bean 實(shí)例化構(gòu)造函數(shù)參數(shù)。如果找不到匹配的 bean,則 Spring 將繼續(xù)檢查下一個(gè)構(gòu)造函數(shù)。
如果沒(méi)有任何構(gòu)造函數(shù)可以匹配,則 Spring 將拋出一個(gè)異常。但是,可以使用 @Autowired(required=false) 注解來(lái)取消必需的依賴關(guān)系,這意味著如果找不到與構(gòu)造函數(shù)參數(shù)類(lèi)型匹配的bean,則 Spring 將不會(huì)拋出異常。
以下是一個(gè)簡(jiǎn)單的例子,說(shuō)明如何在 Spring 中使用構(gòu)造函數(shù)推斷:
public class MyBean { private final MyDependency myDependency; public MyBean(MyDependency myDependency) { this.myDependency = myDependency; } public void doSomething() { myDependency.doSomething(); } }
在這個(gè)例子中,MyBean依賴于MyDependency。Spring 將使用構(gòu)造函數(shù)推斷自動(dòng)注入 MyDependency,而不需要在XML或Java配置文件中顯式指定。
@Configuration public class AppConfig { @Bean public MyDependency myDependency() { return new MyDependency(); } @Bean public MyBean myBean() { return new MyBean(myDependency()); } }
在這個(gè)例子中,AppConfig 類(lèi)使用 @Configuration 注解指定一個(gè) Spring 應(yīng)用程序上下文,并定義兩個(gè)bean:MyDependency 和 MyBean。在 MyBean 的構(gòu)造函數(shù)中,我們將 MyDependency 作為參數(shù)傳遞,Spring 將使用構(gòu)造函數(shù)推斷自動(dòng)注入 MyDependency。
構(gòu)造函數(shù)推斷的限制
構(gòu)造函數(shù)推斷有一些限制,例如:
如果有多個(gè)構(gòu)造函數(shù)可以匹配,則 Spring 將拋出一個(gè)異常。在這種情況下,您需要使用 @Qualifier 注解來(lái)指定要注入的 bean。
如果構(gòu)造函數(shù)參數(shù)是原始類(lèi)型(例如int、float、boolean等),則 Spring 無(wú)法推斷它們。在這種情況下,您需要使用 @Value 注解將值直接注入到構(gòu)造函數(shù)參數(shù)中。
如果構(gòu)造函數(shù)參數(shù)是數(shù)組或集合,則 Spring 無(wú)法推斷它們。在這種情況下,您需要使用 @Qualifier 注解和 @Autowired 的 List 或 Set 類(lèi)型的字段來(lái)手動(dòng)注入數(shù)組或集合。
源碼解析
Spring 使用 BeanWrapperImpl 類(lèi)來(lái)執(zhí)行構(gòu)造函數(shù)推斷。BeanWrapperImpl 類(lèi)是 Spring 的核心類(lèi)之一,它提供了一種方便的方式來(lái)對(duì) Java 對(duì)象進(jìn)行包裝和訪問(wèn)。以下是 Spring 中用于執(zhí)行構(gòu)造函數(shù)推斷的 BeanWrapperImpl 類(lèi)的源代碼:
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper { // ... @Override public Object createInstance() throws BeansException { // 獲取Bean的構(gòu)造函數(shù) Constructor<?> constructorToUse = this.constructorResolver.autowireConstructor; if (constructorToUse == null) { throw new BeansException("No default constructor found"); } try { // 使用構(gòu)造函數(shù)創(chuàng)建Bean實(shí)例,并返回 return constructorToUse.newInstance(getConstructorArgumentValues(constructorToUse), getBeanClassLoader()); } catch (Throwable ex) { throw new BeanCreationException("Could not instantiate bean", ex); } } // ... private Constructor<?> autowireConstructor; // ... public BeanWrapperImpl(Class<?> clazz) { super(); this.objectType = clazz; this.propertyEditorRegistry = new SimpleTypeConverter(); this.constructorResolver = new ConstructorResolver(this.objectType, this); } // ... private class ConstructorResolver { // ... public ConstructorResolver(Class<?> clazz, BeanWrapperImpl bw) { // 查找可以使用的構(gòu)造函數(shù)并將其緩存 Constructor<?>[] candidates = clazz.getDeclaredConstructors(); Constructor<?> autowireCandidate = null; int numAutowireCandidates = 0; for (Constructor<?> candidate : candidates) { if (candidate.isAnnotationPresent(Autowired.class)) { autowireCandidate = candidate; numAutowireCandidates++; } } if (numAutowireCandidates == 1) { this.autowireConstructor = autowireCandidate; } else if (numAutowireCandidates > 1) { throw new BeansException("Multiple autowire constructors found"); } } // ... } // ... }
在BeanWrapperImpl 類(lèi)中,構(gòu)造函數(shù)推斷是在 createInstance() 方法中執(zhí)行的。該方法首先獲取與Bean匹配的構(gòu)造函數(shù)(由constructorResolver.autowireConstructor決定),然后使用該構(gòu)造函數(shù)創(chuàng)建 Bean 實(shí)例。
在 ConstructorResolver 內(nèi)部類(lèi)中,構(gòu)造函數(shù)推斷是通過(guò)查找?guī)в?@Autowired 注解的構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)的。如果找到了一個(gè)帶有 Autowired注解 的構(gòu)造函數(shù),則它將被緩存到 autowireConstructor 字段中,并在 createInstance() 方法中使用。
結(jié)論
構(gòu)造函數(shù)自動(dòng)注入是 Spring 的一個(gè)非常強(qiáng)大的功能,它可以大大簡(jiǎn)化 bean 的配置和管理。我們深入探討了 Spring 構(gòu)造函數(shù)推斷的底層原理,并解釋了 Spring 是如何實(shí)現(xiàn)它的,還提供了一個(gè)簡(jiǎn)單的例子和源代碼解析,以幫助大家更好地了解構(gòu)造函數(shù)推斷的工作方式。
以上就是一文詳解Spring構(gòu)造函數(shù)推斷的詳細(xì)內(nèi)容,更多關(guān)于Spring 構(gòu)造函數(shù)推斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Java實(shí)現(xiàn)音樂(lè)播放器的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java編寫(xiě)一個(gè)簡(jiǎn)單的音樂(lè)播放器,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-07-07一文搞懂Java設(shè)計(jì)模式之責(zé)任鏈模式
這篇文章主要給大家介紹了關(guān)于Java設(shè)計(jì)模式之責(zé)任鏈模式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12SpringBoot中的static靜態(tài)資源訪問(wèn)、參數(shù)配置、代碼自定義訪問(wèn)規(guī)則詳解
這篇文章主要介紹了SpringBoot的static靜態(tài)資源訪問(wèn)、參數(shù)配置、代碼自定義訪問(wèn)規(guī)則,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML
這篇文章主要介紹了JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12httpclient的disableConnectionState方法工作流程
這篇文章主要為大家介紹了httpclient的disableConnectionState方法工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Java并發(fā)編程(CyclicBarrier)實(shí)例詳解
這篇文章主要介紹了Java并發(fā)編程(CyclicBarrier)實(shí)例詳解的相關(guān)資料,JAVA編寫(xiě)并發(fā)程序的時(shí)候,我們需要仔細(xì)去思考一下并發(fā)流程的控制,如何讓各個(gè)線程之間協(xié)作完成某項(xiàng)工作。2017-07-07