Spring系列第十三講 使用繼承簡化bean配置(abstract & parent)

qwer1030274531發表於2020-11-03

先來看一個案例

ServiceA.java

package com.javacode2018.lesson001.demo12;public class ServiceA {}1234

ServiceB.java

package com.javacode2018.lesson001.demo12;public class ServiceB {
    private String name;
    private ServiceA serviceA;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public ServiceA getServiceA() {
        return serviceA;
    }
    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
    @Override
    public String toString() {
        return "ServiceB{" +
                "name='" + name + '\'' +
                ", serviceA=" + serviceA +
                '}';
    }}123456789101112131415161718192021222324252627282930

上面類中有2個屬性,下面我們再建立一個ServiceC類,和ServiceB中的內容一樣。

ServiceC.java

package com.javacode2018.lesson001.demo12;public class ServiceC {
    private String name;
    private ServiceA serviceA;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public ServiceA getServiceA() {
        return serviceA;
    }
    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
    @Override
    public String toString() {
        return "ServiceC{" +
                "name='" + name + '\'' +
                ", serviceA=" + serviceA +
                '}';
    }}123456789101112131415161718192021222324252627282930

下面我們使用spring來建立上面3個類對應的bean。

beanExtend.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="serviceA" class="com.javacode2018.lesson001.demo12.ServiceA"/>
    <bean id="serviceB" class="com.javacode2018.lesson001.demo12.ServiceB">
        <property name="name" value="路人甲Java"/>
        <property name="serviceA" ref="serviceA"/>
    </bean>
    <bean id="serviceC" class="com.javacode2018.lesson001.demo12.ServiceB">
        <property name="name" value="路人甲Java"/>
        <property name="serviceA" ref="serviceA"/>
    </bean></beans>12345678910111213141516171819

建立測試用例。

BeanExtendTest.java

package com.javacode2018.lesson001.demo12;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;/**
 * bean定義繼承案例
 */public class BeanExtendTest {
    @Test
    public void normalBean() {
        String beanXml = "classpath:/com/javacode2018/lesson001/demo12/normalBean.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
        System.out.println("serviceB:" + context.getBean(ServiceB.class));
        System.out.println("serviceC:" + context.getBean(ServiceC.class));
    }}1234567891011121314151617

執行輸出

serviceB:ServiceB{name='路人甲Java', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}serviceC:ServiceC{name='路人甲Java', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}12

透過繼承最佳化程式碼

我們再回頭去看一下上面xml中,serviceB和serviceC兩個bean的定義如下:

<bean id="serviceB" class="com.javacode2018.lesson001.demo12.ServiceB">
    <property name="name" value="路人甲Java"/>
    <property name="serviceA" ref="serviceA"/></bean><bean id="serviceC" class="com.javacode2018.lesson001.demo12.ServiceC">
    <property name="name" value="路人甲Java"/>
    <property name="serviceA" ref="serviceA"/></bean>123456789

這2個bean需要注入的屬性的值是一樣的,都需要注入name和serviceA兩個屬性,並且2個屬性的值也是一樣的,我們可以將上面的公共的程式碼抽取出來,透過spring中繼承的方式來做到程式碼重用。

可以將上面xml調整一下,我們來新建一個extendBean.xml,內容如下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="serviceA" class="com.javacode2018.lesson001.demo12.ServiceA"/>
    <bean id="baseService" abstract="true">
        <property name="name" value="路人甲Java"/>
        <property name="serviceA" ref="serviceA"/>
    </bean>
    <bean id="serviceB" class="com.javacode2018.lesson001.demo12.ServiceB" parent="baseService"/>
    <bean id="serviceC" class="com.javacode2018.lesson001.demo12.ServiceC" parent="baseService"/></beans>123456789101112131415161718

上面多了一個baseService的bean,這個bean沒有指定class物件,但是多了一個abstract="true"的屬性,表示這個bean是抽象的,abstract為true的bean在spring容器中不會被建立,只是會將其當做bean定義的模板,而serviceB和serviceC的定義中多了一個屬性parent,用來指定當前bean的父bean名稱,此處是baseService,此時serviceB和serviceC會繼承baseService中定義的配置資訊。

來個測試用例看一下效果:

@Testpublic void extendBean() {
    String beanXml = "classpath:/com/javacode2018/lesson001/demo12/extendBean.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
    System.out.println("serviceB:" + context.getBean(ServiceB.class));
    System.out.println("serviceC:" + context.getBean(ServiceC.class));}1234567

執行輸出:

serviceB:ServiceB{name='路人甲Java', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}serviceC:ServiceC{name='路人甲Java', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}12

輸出和上面案例的輸出基本一致。

但是這次bean xml中bean的定義簡化了很多,將公共的bean配置提取出來了,透過parent屬性來配置需要繼承的bean。

子bean中也可以重新定義父bean中已經定義好的配置,這樣子配置會覆蓋父bean中的配置資訊,我們將extendBean.xml中serviceC的定義改一下:

<bean id="serviceC" class="com.javacode2018.lesson001.demo12.ServiceC" parent="baseService">
    <property name="name" value="歡迎和【路人甲Java】一起學些spring!"/></bean>123

執行extendBean輸出:

serviceB:ServiceB{name='路人甲Java', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}serviceC:ServiceC{name='歡迎和【路人甲Java】一起學些spring!', serviceA=com.javacode2018.lesson001.demo12.ServiceA@222114ba}12

從輸出中可以看出serviceC中的name對父bean中name的值進行了覆蓋。

我們再來從容器中獲取一下baseService,如下:

System.out.println(context.getBean("baseService"));1

執行輸出:

org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'baseService': Bean definition is abstract
    at org.springframework.beans.factory.support.AbstractBeanFactory.checkMergedBeanDefinition(AbstractBeanFactory.java:1412)123

會報BeanIsAbstractException異常,因為baseService是抽象的,不能夠建立這個bean例項。

總結

bean元素的abstract屬性為true的時候可以定義某個bean為一個抽象的bean,相當於定義了一個bean模板,spring容器並不會建立這個bean,從容器中查詢abstract為true的bean的時候,會報錯BeanIsAbstractException異常

bean元素的parent屬性可以指定當前bean的父bean,子bean可以繼承父bean中配置資訊,也可以自定義配置資訊,這樣可以覆蓋父bean中的配置


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2731827/,如需轉載,請註明出處,否則將追究法律責任。

相關文章