1. 程式人生 > >【Spring學習05】四種xml配置注入方式

【Spring學習05】四種xml配置注入方式

平常應用Spring開發中,用得最多的是容器。spring容器幫我們例項化物件並且注入到需要該物件的類中。
spring有多種依賴注入的形式,下面僅介紹spring通過xml進行IOC配置的方式:

一、Setter注入

set注入是最簡單常用的注入方式,《【Spring學習03】Spring簡單入門例項 》中就是用的set注入。
Order類中需要用到NotifyService物件,於是定義了一個private的NotifyService成員變數,然後建立了名為setNotifyService的set方法,這個set方法就是注入的入口。
setNotifyService方法命名是有講究的。(基於在配置檔案中,我們定義了注入的屬性:<property name="notifyservice" ref="notify"/>

)。
set必須小寫,後面跟的第一個字母大小寫不限,再後面則必須要和xml配置中的property name一致。因此我們也可以命名為setNotifyservice,但不能命名成setNotifyService。

程式碼如下:

public class Order {
    /*要注入的物件*/
    private NotifyService notifyservice;
    /*notifyservice不是在內部new()出來的,
    而是通過指定方法傳進來的,也就是我們說的注入。這裡是setter方法注入*/
    public void setNotifyservice
(NotifyService notifyservice) { this.notifyservice = notifyservice; } /*訂單支付完成後,系統通知老闆*/ public void PaySuccess(){ notifyservice.sendMessage("客戶張鐵蛋完成訂單2017079657付款,共人民幣:97.5元"); } }

然後編寫spring的xml檔案:

<bean id="notify" class="twm.spring.start.NotifyServiceByCellPhoneImpl"
/>
<bean id="order" class="twm.spring.start.Order" > <!-- 配置要注入的物件 --> <property name="notifyservice" ref="notify"/> </bean>

<bean>中的id屬性是bean的標識,必須唯一。class屬性是類的完全限定名,指明由哪個類來例項化。
<property>是用來指明要注入的屬性和物件的。因為我們要注入Order中的私有成員notifyService,所以要在<bean>標籤中建立一個<property>標籤指定notifyService物件。<property>標籤中的name就是就是注入方法名去掉前面的set,剩下的這一截首字母大小寫可不限。ref指向要注入的物件(id)。物件是引用例項,所以要用ref,如果是傳值,則用value。

另外要注意的是:property name只和注入方法名相關,和內部屬性名沒有半毛關係。所以order類改成下面這樣也是可以的:

public class Order {
    private NotifyService notifyservice_suibian;
    public void setnotifyservice(NotifyService notifyservice) {
        this.notifyservice_suibian = notifyservice;
    }

    /*訂單支付完成後,系統通知老闆*/
    public void PaySuccess(){
        notifyservice_suibian.sendMessage("客戶張鐵蛋完成訂單2017079657付款,共人民幣:97.5元");
    }
}

二、構造器注入

建構函式也是一個注入點。現在我們把上例的setter注入改為構造注入。
首先刪除setNotifyservice方法,改成建構函式:

/*建構函式注入*/
public Order(NotifyService notifyservice1) {
    this.notifyservice1 = notifyservice1;
}

相應的beans.xml修改:
<property name="notifyservice" ref="notify"/>
改為<constructor-arg ref="notify"></constructor-arg>
這樣就完成了構造器注入。

<constructor-arg>顧名思義就是建構函式引數,因為這裡只有一個引數,所以直接指向需要注入的物件bean id就行了。
多個引數時,怎麼配置呢?比如我們把order修改為:

public class Order {
    private String username;
    private String orderno;
    private NotifyService notifyservice1;

    /*建構函式注入*/
    public Order(String username, String orderno, NotifyService notifyservice1) {
        this.username = username;
        this.orderno = orderno;
        this.notifyservice1 = notifyservice1;
    }

    /*訂單支付完成後,系統通知老闆*/
    public void PaySuccess(){
        notifyservice1.sendMessage("客戶"+username+"完成訂單"+orderno+"付款,共人民幣:97.5元");
    }
}

這時配置beans.xml就有講究了,spring提供了幾種方法:
1、智慧識別

<bean id="order" class="twm.spring.start.Order" >
    <constructor-arg value="張老三"></constructor-arg>
    <constructor-arg ref="notify"></constructor-arg>
    <constructor-arg value="1234567"></constructor-arg>
</bean>

<constructor-arg>沒有別的屬性。Spring這時會先按型別排序,同類型的按先後順序向建構函式引數賦值。所以如果完全按照建構函式的引數順序寫,肯定是沒有問題的。上面這樣寫,也是沒有問題的,兩個String型別的引數順序對了就行。

2、指明引數型別。

<bean id="order" class="twm.spring.start.Order" >
    <constructor-arg type="String" value="張老三"></constructor-arg>
    <constructor-arg type="twm.spring.start.NotifyService" ref="notify"></constructor-arg>
    <constructor-arg type="String" value="1234567"></constructor-arg>
</bean>

和1智慧識別一樣,Spring會先按型別區分,同類型的按先後順序向建構函式引數賦值。

3、指定引數名

<bean id="order" class="twm.spring.start.Order" >
    <constructor-arg name="username" value="張老三"></constructor-arg>
    <constructor-arg name="notifyservice1" ref="notify"></constructor-arg>
    <constructor-arg name="orderno" value="1234567"></constructor-arg>
</bean>

這樣Spring會完全按照建構函式的形參名字匹配。

4、指定索引index

<bean id="order" class="twm.spring.start.Order" >
    <constructor-arg index="0" value="張老三"></constructor-arg>
    <constructor-arg index="2" ref="notify"></constructor-arg>
    <constructor-arg index="1" value="2017877997"></constructor-arg>
</bean>

指定的索引順序一定要和型別匹配,不然會報錯。建構函式有2個相同型別的引數,指定索引可以解決此種情況。
注意index是從0開始。

構造注入對比setter注入

何時使用構造注入,何時使用setter注入,經驗法則是:強制依賴用構造,可選依賴用Setter。注意,在settter方法上使用@Required註解即可令屬性強制依賴。

Spring 團隊建議,構造注入的例項是不可變的,不為null的。此外,構造注入元件要將完全初始化後的例項返回給客戶端程式碼。還有,大量引數的建構函式是非常爛的,它意味著該類有大量的職責,得重構。

setter注入主要用於可選依賴,類內部可以指定預設依賴。否則類內所有使用依賴的地方,都得進行非空校驗。setter注入的有個好處就是,類可以重配置或者再注入。因此,使用JMXMBeans進行管理的場景中,就非常適合setter注入。

使用何種依賴注入方式,對於某些類,非常有意義。有時協同第三方類處理,沒有原始碼,由你來決定使用何種方式。比如,第三方類未暴露任何setter方法,那麼構造注入也許就是唯一的可行的注入方式了。

三、靜態工廠的方法注入

靜態工廠,就是通過呼叫靜態工廠的方法來獲取自己需要的物件,為了讓spring管理所有物件,我們不能直接通過”工程類.靜態方法()”來獲取物件,而是依然通過spring注入的形式獲取。
在上例的基礎上,增加一個工廠類:

NotifyFactory.java:

public class NotifyFactory {
    /*靜態工廠方法*/
    public static NotifyService getNotifyService(){
        return new NotifyServiceByWeixinImpl();
    }
}

其它類不變,但xml會發現有很大差別:

<bean id="order" class="twm.spring.start.Order">
    <constructor-arg name="username" value="張老三"></constructor-arg>
    <constructor-arg name="notifyservice1" ref="notify2"></constructor-arg>
    <constructor-arg name="orderno" value="1234567"></constructor-arg>
</bean>

<bean id="notify2" class="twm.spring.start.NotifyFactory"
    factory-method="getNotifyService" />

注意看指向的class並不是NotifyService的實現類,而是指向靜態工廠NotifyFactory,並且配置factory-method="getNotifyService"指定呼叫哪個工廠方法:

靜態工廠方法需要引數,怎麼傳遞?
文件上告訴我們靜態工廠方法的引數,應該通過constructor-arg元素產生,就像是bean的建構函式一樣

四、例項工廠的方法注入

例項工廠的意思是獲取物件例項的方法不是靜態的,所以需要首先new工廠類,再呼叫普通的例項方法::

public class NotifyFactory {
    /*普通工廠方法*/
    public NotifyService getNotifyService(){
        return new NotifyServiceByWeixinImpl();
    }
}

例項工廠方法(非靜態)和靜態工廠方法本質相同(除了使用facory-bean屬性替代class屬性,其他都相同),因此細節就不討論了。

<bean id="order" class="twm.spring.start.Order">
    <constructor-arg name="username" value="張老三"></constructor-arg>
    <constructor-arg name="notifyservice1" ref="notify2"></constructor-arg>
    <constructor-arg name="orderno" value="1234567"></constructor-arg>
</bean>


<bean id="notifyfactory" class="twm.spring.start.NotifyFactory" />
<bean id="notify2" factory-bean="notifyfactory" factory-method="getNotifyService" />

Spring IOC注入方式用得最多的是1、2兩種。

現在常用的註解注入不屬於xml配置注入方式,故後面開篇單講

附錄:
1、給屬性設定空格字串(“”)

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

上面的配置相當於exampleBean.setEmail("")

2、給屬性設定null值

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

上面配置相當於exampleBean.setEmail(null)

恭喜你,看完這裡,基本可以應付日常開發了。