1.属性注入
属性注入及通过setXXX()方法注入Bean的属性值或是依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。
属性注入要求Bean提供一个默认的构造函数 ,并为需要注入的属性提供对应的Setter方法Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式,调用Setter方法注入属性值。来看一个简单的例子:
package com.baobaotao.ditype;
public class Car {
private int maxSpeed;
private String brand;
private double price;
public void setBrand(String brand){
this.brand=brand;
}
public void setMaxSpeed(int maxSpeed){
this.maxSpeed=maxSpeed;
}
public void setPrice(double price){
this.price=price;
}
}
Car类定义了3个属性,并分别提供了对应的Setter方法。
注意:默认构造函数是不带参的构造函数。Java语言规定如果类中没有定义任何构造函数,则JVM自动为其生成一个默认的构造函数。 反之,如果类中显式定义了构造函数,则JVM不会为其生成默认的构造函数,如public Car(String brand),则需要同时提供一个默认构造函数public Car(),否则使用属性注入时将抛出异常。
下面是在Spring配置文件中对Car进行属性注入的配置片段:
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>红旗CA72</value></property>
<property name="price"><value>20000.00</value></property>
</bean>
我们配置了一个Bean,并为该Bean的三个属性提供了属性值。具体来说,Bean的每一个属性对应一个< property >标签,name为属性的名称,在Bean实现类中拥有与其对应的Setter方法:maxSpeed对应setMaxSpeed(),brand对应setBrand()。
需要指出的是:Spring只会检查Bean中是否有对应的Setter方法,至于Bean中是否有对应的属性变量则不做要求。
举个例子,配置文件中< property name=“brand”>的属性配置项仅要求Car类中拥有setBrand()方法,但是Car类不一定要拥有brand成员变量:
虽然如此,但是在一般情况下,我们还是按照约定俗称的方式在Bean中提供同名的属性变量。
2.构造函数注入
构造函数注入是除属性注入之外的另一种常用的注入方式,它保证一些必要的属性在Bean实例化时就得到设置,它保证了Bean实例在实例化之后就可以使用。
2.1 按类型匹配入参
使用构造函数注入的前提是Bean必须提供带参的构造函数,下面我们为Car提供一个可设置brand和price属性的构造函数:
package com.baobaotao.ditype;
public class Car{
public Car(String brand,double price){
this.brand=brand;
this.price=price;
}
}
构造函数注入的配置方式和属性注入方式的配置有所不同,下面我们在Spring配置文件中使用构造函数注入装配这个Car Bean:
<bean id="car1" class="com.baobaotao.ditype.Car">
<constructor-arg type="java.lang.String">//注意这个type属性
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
</bean>
在< constructor-arg >的元素中有一个type属性,它为Spring提供了判断配置项和构造函数入参对应关系的“信息”。
那么,配置文件中 < bean >元素的< constructor-arg >声明顺序难道不可以用于确定函数入参的顺序吗?在只有一个构造函数的情况下当然是是可以的,如果我们在Car中定义了多个具有相同数量入参的构造函数,这种顺序标识方法就失效了。此外,Spring的配置文件采用和元素标签顺序无关的策略,这种策略可以一定程度上保证配置信息的正确性,所以两个constructor-arg type的位置并不会对最终的配置效果产生影响。
2.2 按索引匹配入参
我们知道,Java语言通过入参的类型及顺序区分不同的重载方法。对于Car类,Spring仅通过type属性指定的参数类型就可以知道“红旗CA72”对应Spring类型的brand入参。而“20000”对应double类型的price入参。但是如果Car构造函数两个入参的类型相同,我们就无法通过type确定对应关系了,这时需要通过入参索引的方式进行先行确定。
我们知道,在属性注入时,Spring按JavaBean规范确定配置属性和对应的Setter方法,并使用Java反射机制调用Setter方法完成属性注入。但Java的反射机制并不会记住构造函数的入参名,因此我们无法通过指定构造函数的入参名进行构造函数注入的配置,而只能通过入参类型和索引信息简介确定构造函数配置项和入参的对应关系:
为了更好地演示按索引匹配入参的配置方式,我们特意对Car的构造函数进行以下的调整:
public Car(String brand,String corp,double price){
this.brand=brand;
this.corp=corp;
this.price=price;
}
brand和corp的入参类型都是String,所以Spring将无法确定type为String的< constructor-arg >到底对应的是brand还是corp。但是通过显式指定参数的索引能够消除这种不确定性:
<bean id="car2" class=com.baobaotao.ditype.Car">
<constructor-arg index="0" value="红旗CA72"/>
<constructor-arg index="1" value="中国一汽"/>
<constructor-arg index="2" value="20000"/>
</bean>
2.3联合使用类型和索引匹配入参
有时需要type和index联合出马才能确定配置项和构造函数入参的对应关系,来看下面的例子:
public Car(String brand,String corp,double price){
this.brand=brand;
this.corp=corp;
this.price=price;
}
public Car(String brand,String corp,int maxSpeed){
this.brand=brand;
this.corp=corp;
this.maxSpeed=maxSpeed;
}
这里,Car拥有两个重载的构造函数,他们都有3个入参。这时按照入参位置索引的配置方式针对这种情况又难以满足需求了,这时需要联合使用< constructor-arg>的type以及index才能解决。
<bean id="car3" class="com.baobaotao.ditype.Car">
<constructor-arg index="0" type="java.lang.String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg index="1" type="java.lang.String">
<value>中国一汽</value>
</constructor-arg>
<constructor-arg index="2" type="int">
<value>200</value>
</constructor-arg>
</bean>
对于上述这种两个构造函数的情况,如果仅通过index进行配置,Spring将无法确定第三个入参配置究竟是对应int的maxSpeed还是double的price,所以采用索引匹配配置时,真正引起歧义的地方在于第三个入参,因此仅需要明确指定第三个入参的类型就可以消除歧义了。
2.4通过自身类型反射匹配入参
由于Java反射机制可以获取构造函数的入参类型,即使构造函数注入的配置不提供类型和索引的信息,Spring依旧可以正确地完成构造函数的注入工作。下面Boss类构造函数的入参就是可识别的:
public Boss(String name,Car car,Office office){
this.name=name;
this.car=car;
this.office=office
}
由于car、office、以及name的入参类型都是可以辨别的,所以无需在构造函数注入配置时指定< constructor-arg>的类型和索引,因此我们可以采用如下的简易的配置方式:
<bean id="boss" class="com.baobaotao.ditype.Boss">
<constructor-arg><value>John</value></constructor-arg>
<constructor-arg><ref bean="car/"></constructor-arg>
<constructor-arg><ref bean="office/"></constructor-arg>
</bean>
<bean id="car" class="com.baobaotao.ditype.Car/">
<bean id="office" class="com.baobaotao.ditype.Office/">