Spring-Context之九:在bean定義中使用繼承

黃博文發表於2014-04-03

定義bean時有個abstract屬性,可以設定為true或false,預設為false。

1
2
3
4
<bean id="animal" class="Animal" abstract="true">
        <property name="name" value="elephant"/>
        <property name="legs" value="4”/>
</bean>

這裡定義了一個叫elepahnt的animal bean,有4條腿,它與其他bean不同之處是abstract屬性為true。這意味著什麼?意味著這個bean不能被例項化,不能通過ApplicationContext.getBean()的方式來獲取到該bean,也不能使用ref屬性引用這個bean。否則會丟擲BeanIsAbstractException的異常。

你可能會問?坑爹那?宣告一個bean不能被例項化,那有何用?

當然有用,Spring框架開發者也不是一幫吃飽了沒事幹的人,設計一些沒用的功能出來。

這要配合著parent屬性來用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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">

    <bean id="animal" class="Animal" abstract="true">
        <property name="legs" value="4"/>
    </bean>

    <bean id="monkey" parent="animal">
        <property name="name" value="dudu"/>
    </bean>

</beans>

這裡有兩個bean,一個是animal,指定legs是4,另一個是monkey,通過parent的屬性指向animal,指定name為dudu。聰明的讀者可能已經猜出來了,parent屬性就是子bean可以繼承父bean中的屬性,並且在子bean中可以過載對應的屬性。雖然我們沒顯式的指定monkey的legs為4,其實它已經從父bean animal中繼承了這個屬性。這樣的好處是如果在定義大量bean時,發先大量bean存在重複屬性定義時,可以抽取一個抽象bean出來,實現這些重複的屬性定義,讓其他bean都使用parent屬性指向這個抽象bean。這樣可以大大簡化bean的配置。

除了使用parent直接引用父bean的class外,另外也可以使用自定義的class。

Monkey.java
1
2
3
4
5
6
7
8
9
10
11
12
public class Monkey extends Animal {

    private boolean canDrawing;

    public boolean isCanDrawing() {
        return canDrawing;
    }

    public void setCanDrawing(boolean canDrawing) {
        this.canDrawing = canDrawing;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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">

    <bean id="animal" class="Animal" abstract="true">
        <property name="legs" value="4"/>
    </bean>

    <bean id="smartMonkey" class="Monkey" parent="animal">
        <property name="name" value="smallDudu"/>
        <property name="canDrawing" value="true"/>
    </bean>

</beans>

這樣smartMonkey自動繼承了父bean中的legs屬性,同時它的class型別也是一個新型別。

有人可能要問了,子bean的class與父bean中的class一定要是繼承關係嗎?答案是否定的。 請看這個修改後的Monkey class,其本身並未從Animal繼承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Monkey {

    private boolean canDrawing;
    private String name;
    private int legs;

    public boolean isCanDrawing() {
        return canDrawing;
    }

    public void setCanDrawing(boolean canDrawing) {
        this.canDrawing = canDrawing;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLegs() {
        return legs;
    }

    public void setLegs(int legs) {
        this.legs = legs;
    }
}

然後還配置同樣的bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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">

    <bean id="animal" class="Animal" abstract="true">
        <property name="legs" value="4"/>
    </bean>

    <bean id="smartMonkey" class="Monkey" parent="animal">
        <property name="name" value="smallDudu"/>
        <property name="canDrawing" value="true"/>
    </bean>

</beans>

依然能夠正常工作,並且smartMonkey中的legs還是4。

這說明了Spring中使用parent繼承父bean中的屬性並不需要子bean和父bean的class在一個繼承樹上。父bean更像一個模板,子bean能夠自動使用父bean中的配置而已。唯一需要注意的是在父bean中定義的屬性在子bean中都要存在。

那可能有人就有個大膽的猜想了,可不可以定義一個沒有class型別的父bean那?這個bean反正不能例項化,只用來讓子bean繼承屬性。答案是肯定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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">

    <bean id="animal" abstract="true">
        <property name="legs" value="4"/>
    </bean>

    <bean id="monkey" parent="animal" class="Animal">
        <property name="name" value="dudu"/>
    </bean>

    <bean id="smartMonkey" class="Monkey" parent="animal">
        <property name="name" value="smallDudu"/>
        <property name="canDrawing" value="true"/>
    </bean>

</beans>

上面的定義依然可以工作。

多說一點,parent也支援對集合屬性的繼承。比如在父bean中定義了一個屬性為List或Map,子bean中也能繼承到該List或Map,更強大的是子bean還可以對List或Map進行合併。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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">

    <bean id="sampleAccounts" abstract="true">
        <property name="accounts">
            <map>
                <entry key="Bob" value="001"/>
                <entry key="John" value="002"/>
            </map>
        </property>
    </bean>

    <bean id="accountService" parent="sampleAccounts" class="AccountService">
        <property name="accounts">
            <map merge="true">
                <entry key="Michael" value="003"/>
                <entry key="Joel" value="004"/>
            </map>
        </property>
    </bean>

</beans>

在子bean中使用的map元素上使用merge=“true”就可以和父bean中的map條目進行合併。如果指定為false則不會合並,只會使用子bean中定義的map條目。

本例中的原始碼請在我的GitHub上自行下載。

相關文章