在本文中,咱們將介紹IoC(控制反轉)和DI(依賴注入)的概念,而後咱們將看看它們是如何在Spring框架中實現的。編程
控制反轉是軟件工程中的一個原則,經過該原理,對象或程序的一部分的控制被轉移到容器或框架。 它最經常使用於面向對象編程的上下文中。設計模式
與咱們的自定義代碼調用庫的傳統編程相比,IoC使框架可以控制程序流並調用咱們的自定義代碼。 爲了實現這一點,框架使用內置額外行爲的抽象。若是咱們想要添加本身的行爲,咱們須要擴展框架的類或插入咱們本身的類。緩存
這種架構的優勢是:bash
控制反轉能夠經過各類機制實現,例如:策略設計模式,服務定位模式,工廠模式和依賴注入(DI)。架構
依賴注入是一種實現IoC的模式,其中被反轉的控件是對象依賴項的設置。app
將對象與其餘對象鏈接或將對象「注入」其餘對象的行爲由彙編程序而不是對象自己完成。框架
如下是在傳統編程中建立對象依賴項的方法:模塊化
public class Store {
private Item item;
public Store() {
item = new ItemImpl1();
}
}
複製代碼
在上面的示例中,咱們須要在Store類自己中實例化Item接口的實現。函數
經過使用DI,咱們能夠重寫示例而無需指定咱們想要的Item的實現:測試
public class Store {
private Item item;
public Store(Item item) {
this.item = item;
}
}
複製代碼
在下一節中,咱們將瞭解如何經過元數據提供Item的實現。
IoC容器是實現IoC的框架的共同特徵。
在Spring框架中,IoC容器由接口ApplicationContext表示。 Spring容器負責實例化,配置和組裝稱爲bean的對象,以及管理它們的生命週期。
Spring框架提供了ApplicationContext接口的幾個實現 - 用於獨立應用程序的ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,以及用於Web應用程序的WebApplicationContext。
爲了組裝bean,容器使用配置元數據,能夠採用XML配置或註釋的形式。
ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");
複製代碼
要在上面的示例中設置item屬性,咱們可使用元數據。 而後,容器將讀取此元數據並使用它在運行時組裝bean。
Spring中的依賴注入能夠經過構造函數,設置器或字段來完成。
在基於構造函數的依賴項注入的狀況下,容器將調用帶有參數的構造函數,每一個參數表示咱們要設置的依賴項。
Spring主要按類型解析每一個參數,後跟屬性的名稱和消歧的索引。下面使用註釋來查看bean及其依賴項的配置:
@Configuration
public class AppConfig {
@Bean
public Item item1() {
return new ItemImpl1();
}
@Bean
public Store store() {
return new Store(item1());
}
}
複製代碼
@Configuration註釋代表該類是bean定義的來源。 此外,咱們能夠將其添加到多個配置類。
@Bean註釋用於定義bean的方法。若是咱們不指定自定義名稱,則bean名稱將默認爲方法名稱。
對於具備默認單例範圍的bean,Spring首先檢查bean的緩存實例是否已經存在,若是不存在則僅建立新實例。 若是咱們使用原型範圍,容器會爲每一個方法調用返回一個新的bean實例。
下面是經過xml配置建立bean:
<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的DI,在調用無參構造函數或無參數靜態工廠方法來實例化bean以後,容器將調用咱們類的setter方法。讓咱們使用註釋建立此配置:
@Bean
public Store store() {
Store store = new Store();
store.setItem(item1());
return store;
}
複製代碼
相應的xml配置:
<bean id="store" class="org.baeldung.store.Store">
<property name="item" ref="item1" />
</bean>
複製代碼
基於構造函數和基於setter的注入類型能夠組合用於同一個bean。Spring文檔建議對強制依賴使用基於構造函數的注入,對可選依賴使用基於setter的注入。
在基於字段的DI的狀況下,咱們能夠經過使用@Autowired註釋標記它們來注入依賴項:
public class Store {
@Autowired
private Item item;
}
複製代碼
在構造Store對象時,若是沒有構造函數或setter方法來注入Item bean,容器將使用反射將Item注入Store。
這種方法可能看起來更簡單,更清晰,但不推薦使用,由於它有一些缺點,例如:
鏈接容許Spring容器經過檢查已定義的bean來自動解決協做bean之間的依賴關係。
使用XML配置有四種自動裝配bean的模式:
@Bean(autowire = Autowire.BY_TYPE)
public class Store {
private Item item;
public setItem(Item item){
this.item = item;
}
}
複製代碼
咱們還可使用@Autowired註釋注入bean以按類型自動裝配:
public class Store {
@Autowired
private Item item;
}
複製代碼
若是有多個相同類型的bean,咱們可使用@Qualifier批註按名稱引用bean:
public class Store {
@Autowired
@Qualifier("item1")
private Item item;
}
複製代碼
使用xml的方式:
<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>
複製代碼
接下來,讓咱們經過XML將名爲item的bean注入到商店bean的item屬性中:
<bean id="item" class="org.baeldung.store.ItemImpl1" />
<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>
複製代碼
咱們還能夠經過構造函數參數或setter顯式定義依賴關係來覆蓋自動裝配。
默認狀況下,容器在初始化期間建立並配置全部單例bean。 要避免這種狀況,能夠在bean配置中使用值爲true的lazy-init屬性:
<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />
複製代碼
所以,item1 bean只在首次請求時初始化,而不是在啓動時初始化。這樣作的好處是更快的初始化時間,可是帶來的問題是,只有在請求bean以後才能發現配置錯誤,這多是應用程序運行後幾個小時甚至幾天。