spring IOC之依赖注入

来源:互联网 发布:mac怎么打开srt 编辑:程序博客网 时间:2024/04/20 14:58

 

spring配置文档类型

低版本的spring配置文件采用DTD格式,spring2.0中依旧支持DTD格式,但spring推荐使用新的基于

Schema的格式,后者让不同类型的配置拥有了自己的命名空间,使配置文件更具扩展性.此外,spring

基于Schema配置方案为许多领域问题提供了简化的配置方法,配置工作因此得到了大幅简化.

 

===================================================================================================

 

Bean基础配置

 

Bean的命名

一般情况下,在配置一个Bean时,需要为其指定一个id属性作为Bean的名称.在配置文件或程序中提供标识.id在IOC

容器中必须是唯一的,此外,id的命名需要满足xml对id的命名规范:必须以字母开始,后面可以是字母,数字,连字符,

下划线,句号,冒号等完全结束符,逗号和空格这些非完全结束符是非法的.

同时也可以使用<bean>的name属性来命名,name属性没有命名上的限制,几乎可以使用任何字符.

id和name都可以指定多个名字,名字之间可用逗号,分号或者空格进行分隔.

spring配置文件不允许出现两个相同id的<bean>,但却可以出现两个相同name的<bean>,如果有多个name相同的<bean>

通过getBean(beanName)获取Bean时,将返回后面声明的那个Bean,原因是后面的Bean覆盖了前面同名Bean.所以为了

避免无意间Bean覆盖的隐患,应尽量使用id而非name命名Bean.

如果id和name两属性都未指定,如:<bean class="com.baobaotao.simple.Car"/>,spring自动将全限定类名作为

Bean的名称.

但一般情况下,那些奇怪的命名大多都只是唬人的噱头.不值得去实践,通过id为Bean指定唯一名称才是康庄大道.

 

===================================================================================================

 

依赖注入

---------------------------------------------------------------------------------------------------

 

setter注入

 

setter注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的setter方法.spring先调用Bean的默认

构造函数实例化Bean对象,然后通过反射的方式调用setter方法注入属性值.

例:

public class Car{

 private int maxSpeed;

 public void setMaxspeed(int maxSpeed){

  this.maxSpeed=maxSpeed;

 }

}

配置文件中:

<bean id="car" class="com.baobaotao.ditype.Car">

 <property name="maxSpeed" value="200"/>

</bean>

需要指出的是:spring只会检查Bean中是否有对应的setter方法,至于Bean中是否有对应的属性变量则不做要求.也就是根据

property的name属性值去找该值所对应的setter方法.

 

JavaBean关于属性命名的特殊规范

spring配置文件中<property>元素所指定的属性名和Bean实现类的Setter方法满足Sun JavaBean的属性命名规范:xxx的属

性对应setXxx()方法.

一般情况下,Java的属性变量名都以小写字母起头,如:maxSpeed.但也存在特殊的情况,考虑到一些特定意义的大写英文缩略

词(如:USA,XML),JavaBean也允许大写字母起头的属性变量名,不过必须满足: 变量的前两个字母要么全部大写,要么全部小写.

如:iC,iCcard,iDcode这些都不合法的.

如例:

public class Foo{

 private String iDCode;//非法的属性变量名,不过java语言本身不会报错,因为它将iDCode看成普通的变量.

 public void setIDCode(String iDcode){

  this.iDCode=iDcode;//该setter方法对应IDCode属性,而非iDCode属性

 }

}

在spring配置文件中,我们可能会想当然地为Foo提供以下的配置:

<bean id="foo" class="com.baobaotao.attr.Foo">

 <property name="iDCode" value="070101"/>------------这个属性变量名是非法的.

</bean>

这样运行为报错.

 

---------------------------------------------------------------------------------------------------

 

构造函数注入

 

 

按类型匹配入参

如果使用属性注入方式只能人为在配置时提供保证而无法在语法级提供保证,这时通过构造函数注入就可以很好地满足这一

要求.使用构造函数注入的前提是Bean必须提供带参的构造函数.

如:

public class Car{

 ....

 public Car(String brand,double price){

  this.brand=brand;

  this.price=price;

 }

}

构造函数注入的配置方式和属性注入方式的配置有所不同,如例:

<bean id="car1" class="com.baobaotao.ditype.Car">

 <constructor-arg type="java.lang.String">

  <value>大奔</value>

 </constructor-arg>

 <constructor-arg type="double">

  <value>20000</value>

 </constructor-arg>

</bean>

在<constructor-arg>的元素中有一个type属性,它为spring提供了判断配置项和构造函数入参对应关系的"信息".细心的

读者会提出以下疑问:配置文件中<bean>元素的<constructor-arg>声明顺序难道不可以用于确定构造函数入参的顺序吗?

在只有一个构造函数的情况下当然是可以的,如果我们在Car中定义了多个具有相同入参的构造函数,这种顺序标识方法就失效

了.此外,spring的配置文件采用和元素标签顺序无关的策略,这种策略可以在一定程度上保证配置信息的确定性,避免一些似

是而非的问题.

 

 

按索引匹配入参

我们知道Java语言通过入参的类型和顺序判别不同的重载方法.但是如果构造函数如果有两个入参的类型相同,仅通type就无法

确定对应关系了,这时需要通过入参索引的方式进行确定.

如例:

public class 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-org>到底对应的是brand还是corp.

但是通过显式指定参数的索引能够消除这种不确定性.

<bean id="car2" class="com.baobaotao.ditype.Car">

 <constructor-org index="0" value="aaa"/>

 <constructor-org index="1" value="bbb"/>

 <constructor-org index="2" value="ccc"/>

</bean>

构造函数第一个参数索引为0,第二个为1,以此类推.

 

 

联合使用类型和索引匹配入参

有时需要type和index联合出马才能确定配置项和构造函数入参的对应关系.如例:

public class Car{

 ....

 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>aaa</value>

 </constructor-arg>

 <constructor-arg index="1" type="java.lang.String">

  <value>bbb</value>

 </constructor-arg>

 <constructor-arg index="2" type="int">

  <value>eee</value>

 </constructor-arg>

</bean>

 

 

通过自身类型反射匹配入参

如果Bean构造函数入参的类型是可辨别的(非基础数据类型且入参类型各异),由于Java反射机制可以获取构造函数入能的类型

,即使构造函数注入的配置不提供type和index的信息,spring依旧可以正确的完成构造函数的注入工作.

如例:

public class Boss{

 ....

 public Boss(String name,Car car,Office office){

  this.name=name;

  this.car=car;

  this.office=office;

 }

}

由于car,office和name入参的类型都是可辨别的,所以无须在构造函数注入的配置时指定<constructor-org>的类型和索引:

<bean id="boss" class="com.baobaotao.ditype.Boss">

 <constructor-org><value>aaa</value></constructor-org>

 <constructor-org><ref bean="car"/></constructor-org>

 <constructor-org><ref bean="office"/></constructor-org>

</bean>

但是为了避免潜在配置歧义引起的张冠李戴的情况,如果Bean存在多个构造函数,使用显式指定index和type属性依然不失为一

种良好的配置习惯.

 

 

循环依赖问题

spring容器能顺利实例化以构造函数注入进行配置的Bean有一个前提:Bean构造函数入参引用的对象必须已经准备就绪.由于这

个机制的限制,如果两个Bean都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题.

public class Car{

 ...

 public Car(String brand,Boss boss){

  this.brand=brand;

  this.boss=boss;

 }

 ...

}

public class Boss{

 ...

 public Boss(String name,Car car){

  this.name=name;

  this.car=car;

 }

 ...

}

假设我们在spring配置文件中按照以下构造函数注入方式进行配置:

<bean id="car" class="com.baobaotao.cons.Car">

 <constructor-arg index="0" value="aaa"/>

 <constructor-arg index="1" ref="boss"/>

</bean>

<bean id="boss" class="com.baobaotao.coms.Boss">

 <constructor-arg index="0" value="bbb"/>

 <constructor-arg index="1" ref="car"/>

</bean>

当启动spring IOC容器时,因为存在循环依赖问题,spring容器将无法成功启动.如何解决这个问题呢?用户只要修改Bean的代码,

将构造函数注入方式调整为属性注入方式就可以了.

 

---------------------------------------------------------------------------------------------------

 

工厂方法注入

 

非静态工厂方法注入

如例:

...

<bean id="carFactory" class="com.baobaotao.ditype.CarFactory"/>

<bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar"/>

由于CarFactory工厂类的工厂方法不是静态的,所以首先需要定义一个工厂类的Bean,然后通过factory-bean引用工厂类

实例,再通过factory-method指定对应的工厂类方法.

 

静态工厂方法

很多工厂类方法都是静态的,这意味着用户在无须创建工厂类实例的情况下就可以调用工厂类方法,因此,静态工厂方法比非

静态工厂方法的调用更加方便.

当使用静态工厂类型的方法后,用户就无须在配置文件中定义工厂类的Bean了,只须按以下方式进行配置即可:

...

<bean id="car6" class="com.baobaotao.ditype.CarFactor" factory-method="getInstance"/>