Spring学习6(2)
Spring支持三种依赖注入方式,分别是属性注入和构造函数注入,工厂方法注入,这里将了解不同依赖注入函数的具体配置方法。
属性注入
所谓的属性注入就是通过setXXX()方法来注入Bean的属性值或依赖对象,其灵活读高,是实际应用中最长采用的方式。
属性注入实例
属性注入要求Bean提供一个默认的构造函数,并为注入的属性提供对应的Setter方法。过程是先实例化对象,而后用反射的方式调用Setter方法注入属性值。
这里就用car的例子来看:
public void setBrand(String brand) {
this.brand = brand;
System.out.println("调用setBrand()设置属性。");
}
public void setPrice(double price) {
this.price = price;
System.out.println("调用setPrice()设置属性。");
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
System.out.println("调用setMaxSpeed()设置属性。");
}
这里的三个setXXX就是所谓的Setter方法,在beans中的代码如下:
<bean id="car1" class="com.smart.Car"
init-method="myInit"
destroy-method="myDestroy">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>红旗CA72</value></property>
<property name="price"><value>20000.00</value></property>
</bean>
</beans>
上述代码配置了一个Bean并为该Bean的3个属性提供了一个属性值,Bean的每一个属性对应一个<property>
标签,name为属性的名称,并且要求在Bean的实现类有其setter方法。
特别注意的是,spring只会查看是否有setter方法,并不会关注有无这个变量的存在,如下面这种情况:
public class Car{
private int maxSpeed;
private double price;
public void setBrand(String brand){
System.out.println("设置Brand属性");
}
javaBean关于属性命名的特殊规范
Spring配置文件中<property>
元素所指定的属性名和Bean实现类的Setter方法满足属性命名规范:xxx属性对应setXxx()方法。
一般情况下,java的属性变量都用小写字母开头,但是也允许以大写字母开头的属性变量名,不过必须满足变量的前两个字母要么都大写,要么都小写,如brand,IDCode,IC等属性变量名是合法的,而iCcard,iDCode这些是非法的。
下面是一个“违反”了JavaBean属性命名规范的类:
public class Foo{
//非法的属性变量名,不过Java语言本身不会报错,因为它将iDCode看车普通的变量
private String iDCode;
//该方法对应IDCode属性而非iDCode属性
pubic void setIDCode(String iDcode){
this.iDcode = iDcode;
}
}
在配置文件中,我们或许会使用如下的配置,但是这是错误的:
<bean id="foo" class="com.smart.attr.Foo">
<property name="iDCode" value="0000"/>
</bean>
系统报错的原因也是没找到setter方法而不是我们的命名错误,我们需要将配置改成:
<bean id="foo" class="com.smart.attr.Foo">
<property name="IDCode" value="0000"/>
</bean>
所以我们需要尽量的避免这种成员变量的出现。
构造函数注入
构造函数注入是另一种常用的注入方式,它保证在一些必要的属性在Bean实例化时就得到设置,确保Bean在实例化后就可以使用。
按类型匹配入参
如果任何可用对象都必须提供特定成员变量的值,那么只使用属性注入方式则只能人为的在配置时提供保证,而无法提供语法级的保证,所以必须要有构造函数注入。
我们假设有如下这个构造函数的存在
public Car(String brand, double price) {
this.brand = brand;
this.price = price;
}
那么我们在配置文件中使用如下代码进行配置:
<bean id="car2" class="com.smart.Car">
<constructor-arg type="java.lang.String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
</bean>
在<constructor-arg>
元素中有一个type属性,它为Spring提供了判断配置项和构造函数入参对应关系的“信息”,来避免多个具有相同入参的构造函数所造成的问题。
按索引匹配入参
java语言通过入参的类型及顺序区分不同的重载方法。如果car构造函数有两个类型相同的入参那么仅仅通过type就无法对应关系了,这时候就需要通过入参索引的方式进行确定。
下面我们对构造函数进行调整如下:
public Car(String color, double price,String brand) {
this.brand = brand;
this.price = price;
this.color = color;
}
那么我们可以指定索引来避免混淆,索引从0开始。
</bean>
<bean id="car2" class="com.smart.Car">
<constructor-arg index="0" value="black"/>
<constructor-arg index="1" value="20000"/>
<constructor-arg index="2" value="红旗CA72"/>
</bean>
联合使用类型和索引匹配入参
如果有下列两个构造函数就需要联合使用上述两种方法进行配置。
public Car(String brand, String color, double price) {
this.brand=brand;
this.color=color;
this.price=price;
}
public Car(String brand, String color, int maxSpeed) {
this.brand=brand;
this.color=color;
this.maxSpeed=maxSpeed;
}
例如我们要使用第二个构造函数来实例化Bean,则代码如下:
<bean id="car4", class="com.smart.Car">
<construuctor-arg index="0">
<value>红旗XXX</value>
</construuctor-arg>
<constructor-arg index="1">
<value>白色</value>
</constructor-arg>
<constructor-arg index="2" type="int">
<value>200</value>
</constructor-arg>
</bean>
通过自身类型反射匹配入参
如果Bean的构造函数入参的类型非基础数据类型且入参类型各异,那么即使步提供index和type也可以完成注入工作。
循环依赖
Spring容器能对构造函数配置的Bean进行实例化有一个前提即是Bean构造函数入参应用的对象都已经准备就绪。如果两个Bean都采用构造函数并且互为入参,那么就会发生死循环,这时候就需要修改构造函数为setter函数。
工厂方法注入
工厂方法已经成为底层的一部分,不过有些时候仍然需要,所以这里做一个介绍。
非静态工厂方法
对于这类方法必须实例化工厂类后才能调用工厂方法,下面是一个非静态的工厂类方法。
package com.smart.ditype;
public class CarFactory{
public Car createHongQiCar(){
Car car=new Car();
car.setBrand("红旗");
return car;
}
}
工厂类负责创建一个或多个目标实例,一般以接口或抽象类变量的形式放回目标类实例。它对外屏蔽目标类的实例化步骤,调用者甚至不知道目标类。下面的配置就提供了工厂化的注入。
<bean id="carFactory" class="com.smart.ditype.CarFactory"/>
<bean id="car6" factory-bean="carFactory"
factory-method="createHongQiCar"/>
它首先需要定义一个工厂类的Bean,而后通过factory-bean应用工厂类实例,最后通过factory-method指定对应的工厂类方法。
静态工厂方法
相当多的工厂类方法都是静态的,意味着无需实例化就可以使用:
public static Car createHongQiCar(){
...
}
配置如下
<bean id="car7" class="com.smart.ditype.CarFactory" factory-method="createHongQiCar"