常用框架Spring之裝配Bean

本篇介紹Spring中裝配Bean,內容皆總結摘抄自《Spring實戰》,僅作筆記。

在Spring中,對象無需自己查找或創建與其相關聯的其他對象。容器負責將需要相互協作的對象引用賦予各個對象。創建應用對象之間協作關係的行爲稱爲裝配。這也是依賴注入的本質。

Spring提供了三種主要的裝配機制:

  1. XML中進行顯示配置;
  2. Java中進行顯示配置;
  3. 隱式的bean發現機制和自動裝配。

雖然現在基本上都使用的自動裝配,但我們還是介紹一下其他兩種方式增強理解。

通過XML裝配bean

在Spring剛出現的時候,XML是描述配置的主要方式。在使用XML爲Spring裝配bean之前,需要創建一個新的配置規範,即XML文件,並且以<beans>元素爲根。最簡單的Spring XML配置如下所示:

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

</beans>

<beans>是Spring配置文件的根元素。但僅僅是這樣這個文件還沒有什麼作用,我們還需要聲明bean。使用<bean>來聲明bean,可以按照如下方式聲明bean:

<bean id="quest" class="RescueDamelQuest"></bean>

其中class是這個bean的全限定類名,在將這個bean裝配到其他bean中的時候,可以使用id來裝配。例如將RescueDamelQuest bean裝配到DamselRescuingKnight bean中,就可以直接使用其id"quest"來裝配。

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

藉助構造器注入初始化bean

現在我們有DamselRescuingKnight類如下:

public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public DamselRescuingKnight(Quest quest) {
        this.quest = quest;
    }
    public void embartOnQuest(){
    quest.embark();
    }
}

其中Quest是一個接口,RescueDamelQuest類實現了Quest接口。我們現在把RescueDamelQuest bean注入到DamselRescuingKnight bean中,XML配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

當Spring遇到這個<bean>元素時,會創建一個DamselRescuingKnight實例,<constructor-arg>元素會告知Spring要將一個id爲quest的bean引用傳遞到DamselRescuingKnight構造器中。

以上介紹的是將對象的引用裝配到依賴它們的其他對象中,有時候需要用一個字面量值來配置對象,這時我們只需要將<constructor-arg>元素中的ref屬性替換爲value屬性即可,該屬性即表明給定的值以字面量的形式注入到構造器中。

有時還需要裝配集合,例如我們想要注入一個String集合,XML可以配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
    <util:list>
        <value>Janny</value>
        <value>Danny</value>
        <value>LiMing</value>
    </util:list>
</bean>

其中<list>是<constructor-arg>的子元素,表明一個包含值的列表將會傳遞到構造器中,<value>元素用來指定列表中的每個元素。類似的,也可以使用<ref>來替代<value>,實現bean引用列表的裝配。

Setter方法注入

對於有些類可能只有默認的構造器,這時我們可以選擇使用Setter方法注入。先將類修改如下:

public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public void embartOnQuest(){
        quest.embark();
    }
    public Quest getQuest() {
        return quest;
    }
    public void setQuest(Quest quest) {
        this.quest = quest;
    }
}

XML配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <property name="quest" ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

<property>元素爲屬性的Setter方法所提供的功能與<constructor-arg>元素爲構造器提供的功能是一樣的。在上面的XML中,它引用了id爲quest的bean,通過setQuest()方法將其注入到quest屬性中。

在Setter方法注入中同樣可以注入字面量,只需要將<property>元素的ref屬性替換爲value屬性即可。

通過Java代碼裝配bean

使用XML裝配過於繁瑣,尤其是在較大型項目中,XML文件可能會很大。使用JavaConfig可能是一種更好的解決方案。JavaConfig更爲強大、類型安全並且對重構友好。在概念上,它與業務邏輯和領域代碼不同,儘管它同樣使用Java代碼進行表述,但JavaConfig是配置代碼,不應該包含任何業務邏輯,JavaConfig也不應該侵入到業務邏輯代碼中。

創建JavaConfig類的關鍵在於爲其添加@Configuration註解,@Configuration註解表明這個類是一個配置類,該類應該包含在Spring應用上下文中如何創建bean的細節。

要在JavaConfig中聲明bean,需要編寫一個方法,這個方法會創建所需類型的實例,然後給這個方法添加@Bean註解。例如,下面的代碼聲明瞭Knight bean:

@Bean
public Knight knight(){
    return new DamselRescuingKnight();
}

@Bean註解會告訴Spring這個方法將會返回一個對象,該對象要註冊爲Spring應用上下文中的bean,方法體中包含了最終產生bean實例的邏輯。

默認情況下,bean的id與帶有@Bean註解的方法名是一樣的,例如本例中bean的id爲knight,也可以使用name屬性爲其指定另一個名字,例如:

@Bean(name="knightBean")
public Knight knight(){
    return new DamselRescuingKnight();
}

有時我們聲明的bean可能依賴於其他bean,要將他們裝配在一起可以使用如下方式:

@Bean
public Knight knight(){
    return new DamselRescuingKnight(quest());
}
@Bean
public Quest quest(){
    return new RescueDamelQuest();
}

knight()方法中並沒有使用默認構造器來構建實例,而是調用了需要傳入Quest對象的構造器。看起來好像Quest是通過調用quest()得到的,事實上因爲quest()方法上加了@Bean註解,Spring將會攔截所有對它的調用,並確保直接返回該方法鎖創建的bean,而不是每次都對其進行實際的調用。

例如可以再添加另一個Knight bean,與之前的Knight bean完全一樣,如下:

@Bean
public Knight anotherKnight(){
    return new DamselRescuingKnight(quest());
}

默認情況下,Spring中的bean都是單例的,因此兩個Knight bean會得到相同的Quest實例。

自動化裝配bean

最便利的也是現在我們最常用的還是自動化配置,Spring從兩個角度來實現自動化裝配:

  • 組件掃描(component scanning):Spring會自動發現應用上下文中所創建的bean。
  • 自動裝配(autowiring):Spring自動滿足bean之間的依賴。

組件掃描和自動裝配組合在一起能夠將顯示配置降低到最少。

可以在要創建bean的類上使用@Component註解,該註解會告知Spring爲這個類創建bean。但組件掃描默認是不啓用的,因此需要顯式配置一下Spring,從而命令它去尋找帶有@Component註解的類併爲其創建bean。如下配置類可以是完成這項任務的最簡潔配置。

@Configuration
@ComponentScan
public class KnightConfig {
}

KnightConfig類沒有顯示聲明任何bean,但使用了@ComponentScan註解,這個註解能夠在Spring中啓用組件掃描。如果沒有其他配置,@ComponentScan默認會掃描與配置類相同的包以及這個包下的所有子包,查找帶有Component註解的類。還可以使用name屬性爲組件設置id,其方式與JavaConfig中的@Bean類似。

@ComponentScan默認會以配置類所在的包作爲基礎包來掃描組件,但有時我們可能想要掃描不同的包,這時我們可以使用value屬性指定包的名稱,例如:

@ComponentScan("packageName")

如果想更加清晰的表明所設置的是基礎包,可以通過basePackages屬性進行配置:

@ComponentScan(basePackages="packageName")

這個屬性還可以設置多個基礎包,只需要將basePackages屬性設置爲要掃描包的一個數組即可:

@ComponentScan(basePackages={"packageA","packageB"})

自動裝配

自動裝配就是讓Spring自動滿足bean依賴的一種方法,在滿足依賴的過程中,會在Spring應用上下文中尋找匹配某個bean需求的其他bean。爲了聲明要進行自動裝配,我們可以使用Spring中的@Autowired註解。

@Autowired註解可以使用在構造函數、Setter方法和類成員變量上。例如:

@Component
public class DamselRescuingKnight implements Knight{
    private Quest quest;
    @Autowired
    public DamselRescuingKnight(Quest quest) {
        this.quest = quest;
    }
    public void embartOnQuest(){
        quest.embark();
    }
}

構造器上添加了@Autowired註解,表明當Spring創建DamselRescuingKnight bean的時候,會通過這個構造器來進行實例化並且會傳入一個可設置給Quest類型的bean。

在Setter方法上使用@Autowired註解如下:

@Component
public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public void embartOnQuest(){
        quest.embark();
    }
    public Quest getQuest() {
        return quest;
    }
    @Autowired
    public void setQuest(Quest quest) {
    this.quest = quest;
    }
}

事實上,Setter方法沒有特殊之處,@Autowired註解可以用在類的任何方法上,只要這個方法可以與Setter方法發揮的作用相同即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章