在前一篇文章中简单的介绍了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。