Spring框架中實例對象(bean)的創建方式(一)

在前一篇文章中簡單的介紹了SpringIoc(依賴注入/控制反轉)的原理及爲什麼要使用IoC,並且以一個簡單的例子說明了在使用Spring框架時Bean的創建和使用的方式。事實上,Spring提供了豐富的創建實例Bean的方式,大致上可以分爲:指定全類名、工廠方法、基於註解,文章先講解通過指定全類名的方式創建Bean。
首先創建兩個bean類,一個爲Car.jave,另一個爲Person.java,代碼如下:

package cn.pb.bean;
public class Car {
    private String brand;
    private String local;
    public double price;
    private int maxSpeed;

    public Car(String brand, String local, double price) {
        super();
        this.brand = brand;
        this.local = local;
        this.price = price;
    }

    public Car(String brand, String local, int maxSpeed) {
        super();
        this.brand = brand;
        this.local = local;
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
        return "Car [brand=" + brand + ", local=" + local + ", price=" + price
                + ", maxSpeed=" + maxSpeed + "]";
    }   
}
package cn.pb.bean;

public class Person {
    private String name;
    private int age;
    private Car car;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }   
}

注意兩個bean的不同,在Car.java中,有兩個重載的帶參構造方法,沒有無參的構造方法,4個屬性沒有對應的getter和setter,因此只能通過兩個帶參構造方法實例化對象。而在Person.java中,所有的屬性都有對應的getter和setter,並默認有無參的構造方法,這兩種代表了我們在創建對象時的不同的方法,重寫ToString()方法方便查看創建的實例對象。之所以把他們分開,是因爲通過Spring Bean Configuration File來創建Bean的方式也是不同的。
一,Spring容器創建Car實例

<bean id="car" class="cn.pb.bean.Car">
    <constructor-arg value="Audi"></constructor-arg>
    <constructor-arg value="shanghai"></constructor-arg>
    <constructor-arg value="3000000"></constructor-arg>
</bean>

通過此種方法創建Car實例時,Spring容器會找到cn.pb.bean.Car類中的帶參的構造方法併爲參數賦值,創建Car對象,爲了避免順序混亂,也可以如下寫:

<bean id="car" class="cn.pb.bean.Car">
    <constructor-arg value="Audi" index="0"></constructor-arg>
    <constructor-arg value="shanghai" index="1"></constructor-arg>
    <constructor-arg value="3000000" index="2"></constructor-arg>
</bean>

可能你還會有疑問,在Car.java中有兩個帶參的構造方法,他們的第三個參數是不同的,它找的是哪一個呢?可先輸出下結果,測試代碼如下:

package cn.pb.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.pb.bean.Car;

public class ConfigurationTest {
    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("ConfigurationTest.xml");
        Car car=(Car) ctx.getBean("car");
        System.out.println(car);
    }
}

輸出結果:Car [brand=Audi, local=shanghai, price=3000000.0, maxSpeed=0]
可以看出,默認的是第一個構造方法,這與Car中構造方法的順序有關。同時要注意,Spring會自動將int型轉換爲double,卻不會將idouble轉換爲int,因此即使將構造方法Car(String brand, String local, int maxSpeed)置於前面,當constructor-arg標籤index=”2”的項值設置爲double型,創建的也是Car(String brand, String local, double price)對象。爲了避免出現不必要的錯誤,可添加type屬性:

<bean id="car" class="cn.pb.bean.Car">
        <constructor-arg value="Audi" type="String"></constructor-arg>
        <constructor-arg value="shanghai" type="String"></constructor-arg>
        <constructor-arg value="300000" type="double"></constructor-arg>
    </bean>

此時就可以根據類型創建對應的對象,以區分重載的構造方法,注意type值得字母大小寫與類型的對應順序,否則會報錯。
也可以將value屬性提出來放在一個單獨的標籤中,當值中出現特殊字符如”>”、”<”時會報錯,此時可以用

<bean id="car1" class="cn.pb.bean.Car">
    <constructor-arg>
        <value><![CDATA[>BMW<]]></value>
    </constructor-arg>
    <constructor-arg value="guangzhou"></constructor-arg>
    <constructor-arg type="int">
        <value>290</value>
    </constructor-arg>
</bean>

實例化結果:Car [brand=>BMW<, local=guangzhou, price=0.0, maxSpeed=290]
二,Spring容器創建Person實例

<bean id="person" class="cn.pb.bean.Person">
    <property name="name" value="zhangsan"></property>
    <property name="age" value="24"></property>
    <property name="car">
        <bean class="cn.pb.bean.Car">
            <constructor-arg value="Audi" type="String"></constructor-arg>
            <constructor-arg value="shanghai" type="String"></constructor-arg>
            <constructor-arg value="300000" type="double"></constructor-arg>
        </bean>
    </property>
</bean>

實例化結果:Person [name=zhangsan, age=24, car=Car [brand=Audi, local=shanghai, price=300000.0, maxSpeed=0]]
用此種方法創建實例實際上是通過無參的構造方法來創建對象,通過每個屬性所對應的setter方法類進行賦值的,因此在Person類中必須有無參的構造方法和對應的setter,同時應該注意property節點中name屬性的值要與set方法後的名字一致,例如:name=”age”,在Person中的方法是setAge(int age),因爲是通過反射機制來進行賦值的。此對象中有一個屬性是對對象car的引用,因此也可以如下寫:

<bean id="person" class="cn.pb.bean.Person">
    <property name="name" value="zhangsan"></property>
    <property name="age" value="24"></property>
    <property name="car">
        <ref bean="car"/>
    </property>
</bean>

創建的過程還有一種簡化寫法,首先在xml中引入p命名空間,則person還可以改寫爲:

<bean id="person" class="cn.pb.bean.Person" p:age="24" p:name="zhangsan" p:car-ref="car"></bean>

三,在此之外還有自動裝配的方法,可以使用autowire屬性指定自動裝配的方式,byName根據bean的id名稱和當前bean的setter風格的屬性名進行自動裝配 ,例如:

<bean id="person2" class="cn.pb.bean.Person" p:age="30" p:name="Cendy" autowire="byName"></bean>

實例化結果:Person [name=Cendy, age=30, car=Car [brand=Audi, local=shanghai, price=300000.0, maxSpeed=0]]
因爲在Person類中是setCar,所以這裏會自動裝配id名稱爲car的Bean,而不會裝配id名稱爲car1的Bean。
byType則根據bean的類型當前bean的屬性類型進行自動裝配,若IoC容器中有1個以上的類型匹配的bean則拋出異常,如:

<bean id="person2" class="cn.pb.bean.Person" p:age="30" p:name="Cendy" autowire="byType"></bean>

此時會拋出異常,因爲創建創建了id=”car”和id=”car1”兩個Car類型的實例,注掉其中一個如注掉car1,則實例化結果:

Person [name=Cendy, age=30, car=Car [brand=Audi, local=shanghai, price=300000.0, maxSpeed=0]]

以上介紹了Spring中Bean的創建方式,特別的,如果兩個實例之間存在繼承關係,比如id=”person3”的實例與id=”person”實例對象的屬性除姓名之外都一樣,則可以使用bean的parent屬性指定繼承哪個bean的屬性,創建person3的語句可以如下寫:

<bean id="person3" p:name="Tom" parent="person"></bean>

id=”person”實例:Person [name=zhangsan, age=24, car=Car [brand=Audi, local=shanghai, price=300000.0, maxSpeed=0]]
id=”person3”實例:Person [name=Tom, age=24, car=Car [brand=Audi, local=shanghai, price=300000.0, maxSpeed=0]]
當指定的bean中有autowire屬性時,將不會繼承該屬性。給bean添加屬性abstract=”true”,則此bean只能用來被繼承而不能被IoC實例化 ,若某個bean的class屬性沒有指定,則該bean必須是一個抽象bean。

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