Spring 基礎知識之IOC介紹

概述

在本文中,咱們將介紹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的實現。

Spring IOC 容器

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的依賴注入

對於基於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。

這種方法可能看起來更簡單,更清晰,但不推薦使用,由於它有一些缺點,例如:

  • 此方法使用反射來注入依賴項,這比基於構造函數或基於setter的注入代價要高
  • 使用這種方法繼續添加多個依賴項很是容易。 若是你正在使用具備多個參數的構造函數注入,那麼咱們會認爲該類違反單一責任原則。

自動裝配依賴關係

鏈接容許Spring容器經過檢查已定義的bean來自動解決協做bean之間的依賴關係。

使用XML配置有四種自動裝配bean的模式:

  • no:默認值 - 這意味着沒有爲bean使用自動裝配,咱們必須明確地命名依賴項
  • byName:自動裝配是基於屬性的名稱完成的,所以Spring將查找與須要設置的屬性同名的bean
  • byType:相似於byName自動裝配,僅基於屬性的類型。這意味着Spring將查找具備相同類型屬性的bean。若是存在多個該類型的bean,則框架會拋出異常。
  • constructor:自動裝配是基於構造函數參數完成的,這意味着Spring將查找與構造函數參數具備相同類型的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顯式定義依賴關係來覆蓋自動裝配。

beans 延遲初始化

默認狀況下,容器在初始化期間建立並配置全部單例bean。 要避免這種狀況,能夠在bean配置中使用值爲true的lazy-init屬性:

<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />
複製代碼

所以,item1 bean只在首次請求時初始化,而不是在啓動時初始化。這樣作的好處是更快的初始化時間,可是帶來的問題是,只有在請求bean以後才能發現配置錯誤,這多是應用程序運行後幾個小時甚至幾天。

相關文章
相關標籤/搜索