Spring中bean的注入方式


  首先,要學習Spring中的Bean的注入方式,就要先了解什么是依賴注入。依賴注入是指:讓調用類對某一接口的實現類的依賴關系由第三方注入,以此來消除調用類對某一接口實現類的依賴。

  Spring容器中支持的依賴注入方式主要有屬性注入、構造函數注入、工廠方法注入。接下來將為大家詳細介紹這三種依賴注入的方式以及它們的具體配置方法。

 

1.屬性注入

  屬性注入即通過setXXX( )方法注入bean的屬性值或依賴對象。由於屬性注入方式具有可選擇性和靈活性高的特點,因此它也是實際開發中最常用的注入方式。

  Spring首先會調用bean的默認構造函數實例化bean對象,然后再通過反射的方法調用set方法來注入屬性值。

  屬性注入要求bean提供一個默認的構造函數,並且得為需要注入的屬性提供set方法

TIps:所謂默認的構造函數,即不帶參數的構造函數。如果類中沒有自定義任何構造函數,則系統(JVM)會自動生成一個不帶參的默認構造函數,如果類中顯式的自定義了有參數的構造函數,則系統就不會在自動生成默認構造函數,需要自己手動再加一個無參的構造函數。

  下面通過一個實例來演示Spring中bean的屬性注入方式:

編寫一個user類:

package com.Kevin.bean;
/**
 * 創建一個類測試bean的屬性注入方式
 * @author Kevin
 *
 */
public class User {
    
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    

}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="user" class="com.Kevin.bean.User">
        <property name="username">
            <value>Kevin</value>
        </property>
    </bean>
    
</beans>

  其中,每個屬性值對應一個property標簽,name屬性值為類中屬性的名稱。在bean實現類中擁有與其對應的實現方法setUsername( )。

  Tips:Spring只會檢查bean中是否含有setter方法,而對是否有對應的屬性變量則不作具體要求,但按照約定俗成的規則我們最好為其設定相應的屬性變量。

  Spring中<property>標簽的命名規范:

  ● Spring的<property>標簽所指定的屬性名稱和bean實現類的setter方法滿足Sun JavaBean的屬性命名規范,即XXX的屬性對應setXXX( )的方法。

  一般情況下,java的屬性變量名都以小寫字母開頭,但考慮到一些特殊意義的英文縮略詞,java bean也允許一些大寫字母開頭的變量名。但必須滿足以下兩點:

   ● 變量的前兩個字母要么全部大寫,要么全部小寫;

   ● 但以一般編程習慣來說,屬性名最好全部使用小寫字母,方便編程和閱讀。

  對於屬性注入方式來說,只能人為的在配置文件中提供保證,而無法在語法級別提供保證。此時就需要使用構造函數注入這種方式,以此來更好的滿足要求。

 

2.構造函數注入

  構造函數注入是除屬性注入之外的另一種常用的注入方式,它可以保證一些必要的屬性在bean實例化時就得到了設置,並在實例化后就可以使用。

  使用構造函數注入的前提是:bean必須提供帶參的構造函數。

  對於構造函數的注入,配置文件可以有以下幾種方式:

  ●  按類型匹配入參

  ●  按索引匹配入參

  ●  聯合使用類型和索引匹配入參

  ●  通過自身類型反射匹配入參

【按類型匹配入參方式】

編寫bean代碼:

package com.Kevin.bean;
/**
 * 編寫bean測試按類型匹配入參方式
 * @author Kevin
 *
 */

public class Person {
    
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    

}

編寫配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="person" class="com.Kevin.bean.Person">
        <constructor-arg type="String">
            <value>Kevin</value>
        </constructor-arg>
        <constructor-arg type="Integer">
            <value>20</value>
        </constructor-arg>
    </bean>
    
</beans>

  Spring的配置文件采用和元素標簽順序無關的配置策略,因此可以在一定程度上保證配置信息的確定性。

  那么當bean中的構造函數的多個類型參數一樣時,按照類型匹配入參的這種方式容易產生混淆,此時就需要使用另一種方式:按照索引匹配入參。

 

【按照索引匹配入參】

編寫bean代碼:

package com.Kevin.bean;
/**
 * 編寫bean測試按照索引方式入參
 * @author Kevin
 *
 */

public class Student {
    private String name;
    private String gender;
    private Double score;
    public Student(String name, String gender, Double score) {
        super();
        this.name = name;
        this.gender = gender;
        this.score = score;
    }
    

}

配置文件編寫如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="student" class="com.Kevin.bean.Student">
        <constructor-arg index="0" value="Kevin"></constructor-arg>
        <constructor-arg index="1" value="Male"></constructor-arg>
        <constructor-arg index="2" value="66"></constructor-arg>
    </bean>
    
</beans>

Tips:在屬性注入時,Spring按java bean的規范確定配置屬性和對應的setter方法,並使用java反射機制調用屬性的setter方法完成屬性注入。但java反射機制並不會記住構造函數的入參名,因此我們不能通過制定構造函數的入參名稱來進行構造函數的配置,所以我們只能通過入參的類型及索引來間接完成構造函數的屬性注入。

 

【聯合使用類型和索引匹配入參】

  在某些復雜的配置文件當中,需要使用type和index同時出馬才能完成構造函數的參數注入。下面使用一個實例來演示。

編寫bean:

package com.Kevin.bean;
/**
 * 編寫bean測試聯合使用類型和索引匹配入參
 * @author Kevin
 *
 */

public class Teacher {
    private String name;
    private String address;
    private double salary;
    private int age;
    public Teacher(String name, String address, double salary) {
        super();
        this.name = name;
        this.address = address;
        this.salary = salary;
    }
    public Teacher(String name, String address, int age) {
        super();
        this.name = name;
        this.address = address;
        this.age = age;
    }
    

}

  在這個類中,有兩個重載的構造函數,他們都有三個參數,在這種情況下使用type和index的方法都不能完成要求,這時候就需要他們兩個屬性同時使用了。

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="teacher" class="com.Kevin.bean.Teacher">
        <constructor-arg index="0" type="String">
            <value>Kevin</value>
        </constructor-arg>
        <constructor-arg index="1" type="String">
            <value>China</value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>20</value>
        </constructor-arg>
    </bean>
    
</beans>

  可以看到其實重點在於第三個入參的類型,所以我們在配置文件中指定了索引和類型,這樣便可以使得Spring知道對哪個構造函數進行參數注入了。

  Tips:加入我們得配置文件中存在歧義問題,Spring容器是可以正常啟動的,並不會報錯,它將隨機采用一個匹配的構造函數實例化bean。而隨機選擇的構造函數可能並不是用戶所需要的,所以我們在編程時要小心避免出現這種歧義情況。

 

【通過自身類型反射匹配入參】

  如果bean構造函數入參的類型是可辨別的,由於java反射機制可以獲取構造函數入參的類型,即使構造函數的注入不提供類型和索引的信息,Spring依舊可以完成構造函數信息的注入。例如下面實例中Manager類的構造函數的入參類型就是可以辨別的。

編寫Manager類:

package com.Kevin.bean;
/**
 * 編寫Bean測試通過自身類型反射匹配入參方式
 * @author Kevin
 *
 */

public class Manager {
    private String name;
    private Double salary;
    private Person person;
    public Manager(String name, Double salary, Person person) {
        super();
        this.name = name;
        this.salary = salary;
        this.person = person;
    }
    

}

編寫配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="manager" class="com.Kevin.bean.Manager">
        <constructor-arg>
            <value>Kevin</value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="user"/>
        </constructor-arg>
        <constructor-arg>
            <ref bean="person"/>
        </constructor-arg>
    </bean>
    
</beans>

  以上幾種方法都可以實現構造函數參數的注入,但是為了避免問題的發生,還是建議使用顯式的index和type來配置構造函數的入參信息。

 

3.工廠方法注入

  工廠方法是應用中被經常使用的設計模式,也是控制反轉單實例設計思想的主要實現方法。工廠類負責創建一個或多個工廠類實例,工廠類方法一般以接口或抽象類變量的形式返回目標類實例。

  工廠類對外屏蔽了目標類的實例化步驟,調用者甚至根本不用指定具體的目標類是什么。由於Spring容器以框架的方法提供工廠方法的功能,並以透明的方式開放給開發者。因此很少需要手工編寫工程方法。但在一些遺留系統或第三方類庫中還是會碰到工程方法,此時便可以使用Spring工廠注入的方法來進行Spring的注入。

  Spring工廠注入的方法可以分為靜態非靜態兩種。

【非靜態工廠方法】

  有些工廠方法是非靜態的,必須實例化工廠類之后才能調用工廠方法。下面通過一個實例來演示。

編寫工廠類:

package com.Kevin.factorybean;
/**
 * 編寫工廠類測試非靜態工廠方法注入
 * @author Kevin
 *
 */

public class BookFactory {
    public Book buyBook(){
        Book book = new Book();
        book.setName("Think in Java");
        return book;
    }

}

配置文件編寫:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="bookFactory" class="com.Kevin.factorybean.BookFactory"></bean>
    <bean id="book" factory-bean="bookFactory" factory-method="buyBook"></bean>
    
</beans>

  由於bookFactory的工廠方法不是靜態的,因此需要先定義一個工廠類的bean,然后通過factory-bean屬性來引用工廠bean實例。再通過屬性factory-method來指定對應的工廠方法。

【靜態工廠方法】

  很多工廠類方法都是靜態的,這意味着無需創建工廠類實例的情況下就可以調用工廠類方法。因此靜態工程方法比非靜態工廠方法的調用更加方便簡潔。下面通過一個實例來演示靜態工廠方法。

編寫factory類:

package com.Kevin.factorybean;
/**
 * 編寫工廠類測試靜態工廠方法注入
 * @author Kevin
 *
 */

public class CarFactory {
    public static Car createCar(){
        Car car = new Car();
        car.setBrand("Lamborghini");
        return car;
    }

}

編寫配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    <!-- 配置對象 -->
    <bean id="car" class="com.Kevin.factorybean.Car" factory-method="createCar"></bean>
    
</beans>

 

總結

  Spring提供了三種可供選擇的注入方式,但在實際應用中,我們究竟該選擇哪種注入方式,並沒有統一的標准,如下是一些可以參考的理由:

  構造函數注入理由:

  ● 構造函數保證重要屬性預先設置;

  ● 無需提供每個屬性的setter方法,減少類的方法個數;

  ● 可以更好地封裝類變量,避免外部錯誤調用。

  屬性注入理由:

  ● 屬性過多時,構造函數變的臃腫;

  ● 構造函數注入靈活性不強,有時需要為屬性注入null值;

  ● 多個構造函數時,配置上產生歧義,復雜度升高;

  ● 構造函數不利於類的繼承和擴展;

  ● 構造函數注入會引起循環依賴的問題。 

  其實Spring為我們注入參數提供了這么多方法,那么這些方法必然有他們存在的道理,每個方法在某一問題上會有獨特的優勢,我們只需要按照我們具體的使用需求選擇適合的方法來使用就好了,但一般不太推薦工廠方法注入。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM