Spring學習(十八)Bean 的三種依賴注入方式介紹

依賴注入:讓調用類對某一接口實現類的依賴關係由第三方注入,以移除調用類對某一接口實現類的依賴。
接下來將詳細的向大家介紹Spring容器支持的三種依賴注入的方式以及具體配置方法:
•    屬性注入方法
•    構造函數注入方法
•    工廠方法注入方法

一.屬性注入
屬性注入即通過setXXX()方法注入Bean的屬性值或者依賴對象,由於屬性注入方式具有可選擇性和靈活高的優點,因此屬性注入是實際中最常採用的注入方式。
Spring首先會調用bean的默認構造函數實例化bean對象,然後再通過反射的方法來調用set方法來注入屬性值。
屬性注入要求bean提供一個默認的構造函數,並且得爲需要注入的屬性提供set方法。

注意:所謂默認構造函數是指不帶參的構造函數
JAVA中定義:
如果類中沒有定義任何的構造函數,則JAVA虛擬機自動爲其生成一個默認的構造函數。反之,如果類中顯式的定義了構造函數,則JAVA虛擬機便不會在爲其生成構造函數。

來看一段java示例代碼:
Public class Car {
    private String brand;
    public void setBrand(String brand) {
          this.brand = brand;
     }
     public String getBrand() {
          return this.brand;
     }
}
可以看到Car這個類有一個String類型的brand屬性,併爲其提供了setter和getter方法。
我們再來看看與之相對應的屬性注入配置文件:
<bean id=“car” class=“com.jike.***.Car” >
    <property name=“brand”>
         <value>奔馳</value>
     </property>
</bean>
其中每一個屬性值對應一個property標籤,name爲屬性的名稱。
在bean實現類中擁有與其對應的實現方法setBrand()。
注意:Spring只會檢查bean中是否有setter方法,而是否有對應的屬性變量則不做具體要求,但按照約定俗成的規則我們最好爲其設定相應的屬性變量。



補充: Spring <property>元素的命名規範:
Spring配置文件中<property>元素所指定的屬性名和Bean實現類的Setter方法滿足Sun JavaBean的屬性命名規範,即xxx的屬性對應setXxx()的方法。一般情況下,java的屬性變量名都以小寫字母開頭,但考慮到一些特殊意義的英文縮略詞,java bean也允許一些大寫字母開頭的變量名。但必須滿足特點的條件:
變量的前兩個字母要麼全部大寫,要麼全部小寫
但以編程經驗來說:最好屬性名全部使用小寫字母,方便編程。



對於使用屬性注入方法來說,只能人爲在配置文件中提供保證,而無法在語法級別提供保證。那麼這時我們便要介紹如下這種注入方式:構造函數注入,通過這種方法便可以很好的滿足要求。

二.構造函數注入
構造函數注入是除屬性注入之外的另一種常用的注入方式,它保證一些必要的屬性在Bean實例化時就得到了設置,並在實例化後就可以使用。
使用構造函數注入的前提是:bean必須提供帶參的構造函數。
對於構造函數注入,配置文件可以有以下幾種方式:
  • 按類型匹配入參
  • 按索引匹配入參
  • 聯合使用類型和索引匹配入參
  • 通過自身類型反射匹配入參

1.按照類型匹配入參方式:

先來看一下bean 代碼:
public class Car{
    private String brand;
    private double price;
    public Car(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}
我們爲bean編寫配置文件
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
        <constructor-arg type="String">
            <value>紅旗CA72</value>
        </constructor-arg>
        <constructor-arg type="double">
            <value>26666</value>
        </constructor-arg>
</bean>
Spring的配置文件採用和元素標籤順序無關的配置策略。這種策略可以在一定程度上保證配置信息的確定性。

那麼當bean中的構造函數的多個參數類型一樣時,按照類型匹配入參的這種方式便會產生混淆,那麼我們便來看一看另外一種方式:按照索引匹配入參。
2.按照索引匹配入參方法:
先來編寫對應bean的代碼:
public class Car{
    private String brand;
    private String corp;
    private double price;

    public Car(String brand, String corp, double price) {
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }
}
可以看到,在這個bean中的構造函數中存在兩個String類型的參數,那麼我們便使用索引來對其中的參數進行注入。
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
        <constructor-arg index="0" value="紅旗CA72"/>
        <constructor-arg index="1" value="中國一汽"/>
        <constructor-arg index="2" value="2666"/>
</bean>
注意
在屬性注入時,Spring按java bean 的規範確定配置屬性和對應的setter方法,並使用java 反射機制調用屬性的setter方法完成屬性注入。但java反射機制並不會記住構造函數的入參名。因此我們不能通過指定構造函數的入參名稱來進行構造函數的配置,所以我們只能通過入參的類型及其索引來間接的完成構造函數的屬性注入。

3.聯合使用類型和索引匹配入參
在某些複雜的配置文件當中,需要使用type和index同時出馬才能完成構造函數的參數注入。那麼接下里我們看一下下面的這個例子:
public class Car{
    private String brand;
    private String corp;
    private double price;
    private int maxSpeed;

    public Car(String brand, String corp, double price) {
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }

    public Car(String brand, String corp, int maxSpeed) {
        this.brand = brand;
        this.corp = corp;
        this.maxSpeed = maxSpeed;
    }
}
在這個類中,有兩個重載的構造函數,他們都有三個入參,在這種情況下使用type和index的方法都不能完成要求,這時候就需要聯合他們兩個屬性一起使用了。
接下來我們來看一看相應的配置文件:
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
        <constructor-arg index="0" type="String">
            <value>紅旗CA72</value>
        </constructor-arg>
        <constructor-arg index="1" type="String">
            <value>中國一汽</value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>200</value>
        </constructor-arg>
</bean>

可以看到其實重點在於第三個入參的類型,所以我們在配置文件中對其指定了索引和類型,這樣便使得Spring可以知道對那個構造函數進行參數注入了。
注意
假如我們的配置文件中存在歧義問題,Spring容器是可以正常啓動的。並不會報錯,他將隨機採用一個匹配的構造函數實例化bean。而隨機選擇的構造函數可能並不是用戶所需要的,所以我們在編程時必須小心避免出現這種歧義情況的出現。

4.通過自身類型反射匹配入參
如果bean構造函數入參的類型是可辨別的,由於java反射機制可以獲取構造函數入參的類型,即使構造函數的注入不提供類型和索引的信息,Spring依舊可以完成構造函數信息的注入。

下面的示例代碼當中的Boss類的構造函數的入參類型就是可以辨別的:
public class Boss {
    private String name;
    private Car car;
    private Office office;
    public Boss(String name, Car car, Office office) {
        this.name = name;
        this.car = car;
        this.office = office;
    }
}

在其配置文件當中便可以直接配置入參值。
<bean id="Boss" class="cn.lovepi.chapter03.scope.Boss">
        <constructor-arg>
            <value>wang</value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="car"/>
        </constructor-arg>
        <constructor-arg>
            <ref bean="office"/>
        </constructor-arg>
</bean>
以上的幾種方法都可以實現構造函數參數的注入,但是爲了避免問題的發生,建議還是使用顯示的index和type來配置構造函數的入參信息。
三.工廠方法注入方法
工廠方法是應用中被經常使用的設計模式,也是控制反轉單實例設計思想的主要實現方法。
工廠類負責創建一個或多個工廠類實例,工廠類方法一般以接口抽象類變量的形式返回目標類實例。
工廠類對外屏蔽了目標類的實例化步驟,調用者甚至根本不用指定具體的目標類是什麼。
由於Spring容器以框架的方法提供工廠方法的功能,並以透明的方式開放給開發者。因此很少需要手工編寫工程方法。但在一些遺留系統或第三方類庫中還是會碰到工廠方法,此時便可以使用Spring工廠注入的方法來進行Spring的注入。
Spring工廠注入的方法可以分爲靜態非靜態兩種:
1.非靜態工廠方式
有些工廠方法是非靜態的,則必須實例化工廠類後才能調用工廠方法。
下面來看一個代碼示例:
public class CarFactory{
    public Car createHongQiCar(){
        Car car=new Car();
        car.setBrand("紅旗CA72");
        return car;
    }
}

配置文件編寫:
<bean id="carFactory" class="cn.lovepi.chapter02.reflect.CarFactory"/>
<bean id="car" factory-bean="carFactory"
     factory-method="createHongQiCar">
</bean>
由於carFactory的工廠方法不是靜態的,所以首先需要定義一個工廠類的bean,然後通過factory-bean這個屬性來引用工廠bean實例。在通過屬性factory-method來指定對應的工廠方法。
2.靜態工廠方法
很多工廠類方法都是靜態的,這意味在無須創建工廠類實例的情況下就可以調用工廠類方法
因此靜態工廠方法比非靜態工廠方法的調用更加方便
那麼我們來看靜態工廠方法的bean代碼:
public class CarFactory{
    public static Car createCar(){
        Car car=new Car();
        car.setBrand("紅旗CA72");
        return car;
    }
}
則其配置文件爲:
<bean id="car" class="cn.lovepi.chapter02.reflect.Car” factory-method="createCar"></bean>
總結:
Spring提供了三種可供選擇的注入方式,在實際應用中,我們究竟應該選擇哪種注入方式,並沒有統一的標準,如下是一些可以參考的理由:
構造函數注入理由:
  • 構造函數保證重要屬性預先設置
  • 無需提供每個屬性Setter方法,減少類的方法個數
  • 可更好的封裝類變量,避免外部錯誤調用
屬性注入理由:
  • 屬性過多時,構造函數變的臃腫可怕
  • 構造函數注入靈活性不強,有時需要爲屬性注入null值
  • 多個構造函數時,配置上產生歧義,複雜度升高
  • 構造函數不利於類的繼承和擴展
  • 構造函數注入會引起循環依賴的問題

其實,Spring爲我們注入參數提供了這些多方法那麼他們必然有他們在某一問題上的優勢性,那麼我們只需按照我們具體的使用需求選擇適合我們的方法來使用就好了,但在這裏不推薦的方法就是工廠方法注入










發佈了68 篇原創文章 · 獲贊 132 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章