在本文中,我們將介紹
IoC
(控制反轉(zhuǎn))和DI
(依賴注入)的概念,以及如何在Spring
框架中實(shí)現(xiàn)它們。
什么是控制反轉(zhuǎn)?
控制反轉(zhuǎn)是軟件工程中的一個(gè)原則,它將對(duì)象或程序的某些部分的控制權(quán)轉(zhuǎn)移給容器或框架。我們最常在面向?qū)ο缶幊痰纳舷挛闹惺褂盟?/p>
與傳統(tǒng)編程相比,傳統(tǒng)編程中我們的自定義代碼調(diào)用庫,而 IoC 使框架控制程序的流程并調(diào)用我們的自定義代碼。為了實(shí)現(xiàn)這一點(diǎn),框架使用具有附加行為的抽象。如果我們想要添加自己的行為,我們需要擴(kuò)展框架的類或插入自己的類。
這種架構(gòu)的優(yōu)點(diǎn)是:
- 將任務(wù)的執(zhí)行與其實(shí)現(xiàn)分離
- 更容易在不同實(shí)現(xiàn)之間切換
- 程序的更高的模塊化
- 更容易通過隔離組件或模擬其依賴項(xiàng)來測(cè)試程序,并允許組件通過契約進(jìn)行通信
我們可以通過各種機(jī)制實(shí)現(xiàn) IoC
,例如:策略設(shè)計(jì)模式、服務(wù)定位器模式、工廠模式和依賴注入(DI
)。
什么是依賴注入?
依賴注入是一種我們可以用來實(shí)現(xiàn) IoC
的模式,其中被反轉(zhuǎn)的控制是設(shè)置對(duì)象的依賴項(xiàng)。
將對(duì)象與其他對(duì)象連接或?qū)?duì)象“注入”到其他對(duì)象中是由匯編程序而不是對(duì)象本身完成的。
下面是在傳統(tǒng)編程中創(chuàng)建對(duì)象依賴關(guān)系的方法:
public class Store {
private Item item;
public Store() {
item = new ItemImpl1();
}
}
在上面的示例中,我們需要在 Store
類本身中實(shí)例化 Item
接口的實(shí)現(xiàn)。
通過使用 DI
,我們可以重寫該示例,而不指定我們想要的 Item
的實(shí)現(xiàn):
public class Store {
private Item item;
public Store(Item item) {
this.item = item;
}
}
在接下來的幾節(jié)中,我們將看看如何通過元數(shù)據(jù)提供 Item
的實(shí)現(xiàn)。
IoC
和 DI
都是簡單的概念,但它們對(duì)我們構(gòu)建系統(tǒng)的方式有深刻的影響,因此值得充分理解。
Spring IoC容器
IoC
容器是實(shí)現(xiàn) IoC
的框架的常見特征。
在 Spring
框架中,接口 ApplicationContext
表示 IoC
容器。Spring
容器負(fù)責(zé)實(shí)例化、配置和組裝稱為 bean
的對(duì)象,以及管理它們的生命周期。
Spring
框架提供了 ApplicationContext
接口的幾個(gè)實(shí)現(xiàn):ClassPathXmlApplicationContext
和 FileSystemXmlApplicationContext
用于獨(dú)立應(yīng)用程序,以及 WebApplicationContext
用于 Web 應(yīng)用程序。
為了組裝 bean
,容器使用配置元數(shù)據(jù),可以是 XML
配置或注釋形式。
以下是手動(dòng)實(shí)例化容器的一種方法:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
在上面的示例中,我們可以使用元數(shù)據(jù)設(shè)置 item
屬性,然后容器將讀取此元數(shù)據(jù)并在運(yùn)行時(shí)使用它來組裝 bean
。
在 Spring
中,可以通過構(gòu)造函數(shù)、setter
或字段來進(jìn)行依賴注入。
基于構(gòu)造函數(shù)的依賴注入
在基于構(gòu)造函數(shù)的依賴注入的情況下,容器將調(diào)用具有表示我們要設(shè)置的依賴項(xiàng)的參數(shù)的構(gòu)造函數(shù)。
Spring
通過類型解決每個(gè)參數(shù),然后按屬性名稱和索引進(jìn)行消歧。讓我們看看使用注釋配置 bean
及其依賴項(xiàng)的配置:
@Configuration
public class AppConfig {
@Bean
public Item item1() {
return new ItemImpl1();
}
@Bean
public Store store() {
return new Store(item1());
}
}
@Configuration
注釋表示該類是 bean
定義的源。我們也可以將其添加到多個(gè)配置類中。
我們?cè)诜椒ㄉ鲜褂?@Bean
注釋來定義 bean
。如果我們沒有指定自定義名稱,則 bean
名稱將默認(rèn)為方法名稱。
對(duì)于默認(rèn)的 singleton
范圍的 bean
,Spring
首先檢查是否已存在緩存的 bean
實(shí)例,僅在不存在時(shí)創(chuàng)建新實(shí)例。如果我們使用 prototype
范圍,則容器為每個(gè)方法調(diào)用返回一個(gè)新的 bean
實(shí)例。
創(chuàng)建 bean
的另一種方式是通過 XML
配置:
<bean id="item1" class="org.baeldung.store.ItemImpl1" />
<bean id="store" class="org.baeldung.store.Store">
<constructor-arg type="ItemImpl1" index="0" name="item" ref="item1" />
</bean>
## 基于setter的依賴注入
對(duì)于基于 `setter` 的 `DI`,容器將在調(diào)用沒有參數(shù)的構(gòu)造函數(shù)或沒有參數(shù)的靜態(tài)工廠方法來實(shí)例化 `bean` 之后調(diào)用我們類的 `setter` 方法。讓我們使用注釋創(chuàng)建此配置:
```java
@Bean
public Store store() {
Store store = new Store();
store.setItem(item1());
return store;
}
我們也可以使用 XML 進(jìn)行相同的 bean 配置:
<bean id="store" class="org.baeldung.store.Store">
<property name="item" ref="item1" />
</bean>
我們可以將構(gòu)造函數(shù)和 setter
類型的注入結(jié)合在同一個(gè) bean
中。Spring
文檔建議將基于構(gòu)造函數(shù)的注入用于必需的依賴項(xiàng),將基于 setter
的注入用于可選的依賴項(xiàng)。
基于字段的依賴注入
在基于字段的 DI
的情況下,我們可以通過帶有 @Autowired
注釋的注釋將依賴項(xiàng)注入其中:
public class Store {
@Autowired
private Item item;
}
在構(gòu)造 Store
對(duì)象時(shí),如果沒有構(gòu)造函數(shù)或 setter
方法將 Item bean
注入其中,容器將使用反射將 Item
注入 Store
中。
我們也可以使用 XML 來實(shí)現(xiàn)這一點(diǎn)。
這種方法可能看起來更簡單、更清晰,但我們不建議使用它,因?yàn)樗幸恍┤秉c(diǎn),例如:
- 此方法使用反射來注入依賴項(xiàng),這比基于構(gòu)造函數(shù)或
setter
的注入更昂貴。 - 使用此方法很容易添加多個(gè)依賴項(xiàng)。如果我們使用構(gòu)造函數(shù)注入,有多個(gè)參數(shù)會(huì)讓我們認(rèn)為這個(gè)類做了不止一件事,這可能違反單一責(zé)任原則。
自動(dòng)裝配依賴項(xiàng)
自動(dòng)裝配允許 Spring
容器通過檢查已定義的 bean
來自動(dòng)解決協(xié)作 bean
之間的依賴關(guān)系。
使用 XML
配置有四種自動(dòng)裝配 bean
的模式:
-
no
:默認(rèn)值 - 這意味著不使用自動(dòng)裝配,我們必須顯式地命名依賴項(xiàng)。 -
byName
:按屬性名稱進(jìn)行自動(dòng)裝配,因此Spring
將查找與需要設(shè)置的屬性同名的bean
。 -
byType
:類似于按名稱進(jìn)行自動(dòng)裝配,僅基于屬性的類型。這意味著Spring
將查找具有相同類型的屬性來設(shè)置的bean
。如果有多個(gè)bean
具有該類型,則框架會(huì)拋出異常。 -
constructor
:基于構(gòu)造函數(shù)參數(shù)進(jìn)行自動(dòng)裝配,意味著Spring
將查找具有與構(gòu)造函數(shù)參數(shù)相同類型的bean
。
例如,讓我們通過類型創(chuàng)建具有依賴項(xiàng) item
的 store
bean
。
public class AppConfig {
@Bean
public Item item() {
return new ItemImpl1();
}
@Bean(autowire = Autowire.BY_TYPE)
public Store store() {
return new Store();
}
}
請(qǐng)注意,自 Spring 5.1
起,autowire
屬性已棄用。
我們還可以使用 @Autowired
注釋按類型注入 bean
:
public class Store {
@Autowired
private Item item;
}
如果存在相同類型的多個(gè) bean
,則可以使用 @Qualifier
注釋按名稱引用 bean
:
public class Store {
@Autowired
@Qualifier("item1")
private Item item;
}
現(xiàn)在,讓我們通過 XML
配置按類型自動(dòng)裝配 bean
:
<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>
接下來,讓我們通過 XML
按名稱將名為 item
的 bean
注入到 store
bean
的 item
屬性中:
<bean id="item" class="org.baeldung.store.ItemImpl1" />
<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>
我們還可以通過構(gòu)造函數(shù)參數(shù)或 setter
顯式定義依賴關(guān)系來覆蓋自動(dòng)裝配。
惰性初始化的bean
默認(rèn)情況下,容器在初始化期間創(chuàng)建和配置所有單例 bean
。為了避免這種情況,我們可以在 bean
配置上使用值為 true
的 lazy-init
屬性:
<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />
因此,只有在第一次請(qǐng)求它時(shí),才會(huì)初始化 item1
bean
,而不是在啟動(dòng)時(shí)。這樣做的優(yōu)點(diǎn)是初始化時(shí)間更快,但缺點(diǎn)是我們?cè)?bean
被請(qǐng)求之后才會(huì)發(fā)現(xiàn)任何配置錯(cuò)誤,這可能是應(yīng)用程序已運(yùn)行數(shù)小時(shí)甚至數(shù)天之后。
結(jié)論
在本文中,我們介紹了控制反轉(zhuǎn)和依賴注入的概念,并在 Spring
框架中進(jìn)行了示例。
最后
為了方便其他設(shè)備和平臺(tái)的小伙伴觀看往期文章:
微信公眾號(hào)搜索:Let us Coding
,關(guān)注后即可獲取最新文章推送文章來源:http://www.zghlxwxcb.cn/news/detail-760538.html
看完如果覺得有幫助,歡迎 點(diǎn)贊、收藏、關(guān)注文章來源地址http://www.zghlxwxcb.cn/news/detail-760538.html
到了這里,關(guān)于深入理解 Spring IoC 和 DI:掌握控制反轉(zhuǎn)和依賴注入的精髓的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!