3依赖注入
Spring bean
的依赖注入有两种形式,通过构造方法注入和通过set
方法注入。构造方法注入是通过constructor-arg
元素来指定的,而set
方法注入是通过property
元素来指定的。
3.1构造方法注入
先来看一个简单的示例:
<bean id="hello" class="com.app.Hello">
<constructor-arg value="hello"/>
</bean>
上述配置定义了一个类型为com.app.Hello
的bean
,我们通过constructor-arg
指定了其第一个构造参数的值为字符串“hello”
,这就是一个简单的通过构造方法进行注入的示例。
当参数类型为基本数据类型时可以直接通过constructor-arg
元素的value
属性指定对应的值,更确切的讲应该是Spring
能够通过String
进行转换的类型,默认Spring
能将String
转换成对应的基本数据类型,其它的需要自己指定转换规则了。如上面示例中的value=”hello”
就是这样的。
当有多个构造参数时就需要使用多个constructor-arg
进行定义,默认会将定义的顺序作为参数的顺序。
public class Hello {
public Hello(String str1, boolean b2, int i3) {
}
}
如上Class
,我们在定义对应的bean
时可以如下定义,这个时候默认会按照定义的顺序将第一个参数赋值给构造方法的第一个参数,第二个给第二个,第n个给第n个。
<bean id="hello" class="com.app.Hello">
<constructor-arg value="str1"/><!-- str1 -->
<constructor-arg value="true"/><!-- b2 -->
<constructor-arg value="3"/><!-- i3 -->
</bean>
当然,我们也可以明确的利用index
来指定constructor-arg
对应参数的位置,index
是从0开始的,即第一个参数对应的index
为0。使用index
时,Hello
对应的bean
可以如下定义。
<bean id="hello" class="com.app.Hello">
<constructor-arg index="1" value="true"/><!-- b2 -->
<constructor-arg index="0" value="str1"/><!-- str1 -->
<constructor-arg index="2" value="3"/><!-- i3 -->
</bean>
当构造参数对应于ApplicationContext
中的一个bean
时,我们也可以通过ref
属性关联对应的bean
,即注入的不再是普通的字符串,而是对应的bean
。
public class Hello {
public Hello(World world1) {
}
}
<bean id="world" class="com.app.World"/>
<bean id="hello" class="com.app.Hello">
<constructor-arg ref="world"/>
</bean>
我们还可以通过constructor-arg
元素的name
属性来指定对应constructor-arg
对应的参数,如:
public class Hello {
public Hello(String s1, int i2) {
}
}
<bean id="hello" class="com.app.Hello">
<constructor-arg name="i2" value="2"/><!-- 对应参数i2 -->
<constructor-arg name="s1" value="1"/><!-- 对应参数s1 -->
</bean>
但是这需要我们的class
是通过debug
方式进行编译的,这样Spring
才能识别出对应的参数名称。当然我们也可以通过JDK
提供的@ConstructorProperties
注解来指定构造参数对应的名称。如:
public class Hello {
@ConstructorProperties({"s1", "i2"})
public Hello(String s1, int i2) {
}
}
当我们的构造参数比较复杂,比如是一个array、list、set、map
等或者需要定义一个内部的bean
时,我们可以直接在constructor-arg
元素中间进行定义,如:
<bean id="hello" class="com.app.Hello">
<constructor-arg>
<bean class="com.app.World"/>
</constructor-arg>
</bean>
3.2set方法注入
set
方法注入是通过property
元素定义的。定义时我们需要指定property
元素的name
属性,其对应的值并非bean
需要进行注入的属性名称,而是对应set
方法去掉set
前缀后首字母小写的结果。
public class Hello {
private String prop1;
public void setProp(String prop) {
this.prop1 = prop;
}
}
对于上述class
定义,如果现在我们需要定义对应的bean
,并通过setProp()
方法将字符串“Value”
注入给其私有属性prop1
,那么我们的bean
应该如下定义:
<bean id="hello" class="com.app.Hello">
<property name="prop" value="Value"/>
</bean>
在上述示例中property
元素对应的语义是setProp(“value”)
,即我们的注入虽然是通过property
元素进行的,但其不是通过bean
的某属性进行注入的,而是通过对应的set
方法进行注入的。
对于基本数据类型值我们可以直接通过property
元素的value
属性进行注入,如果需要注入其它bean
,我们可以通过在property
元素下定义ref
元素进行引用或者通过property
元素的ref
属性进行引用,也可以在property
元素下定义bean
元素进行注入。对于其它集合类型,如Set、List、Map、Array、Properties
等则可以在property
元素下通过set、list
等元素进行定义。
3.3idref
当我们需要通过构造方法或者set
方法给bean
注入一个普通的字符串类型的值时,我们可以直接进行注入,如下就是直接将字符串“Value1”
通过setP1()
方法注入对应的bean
的示例。
<bean id="hello" class="com.app.Hello">
<property name="p1" value="Value1"/>
</bean>
当我们需要注入的普通字符串是bean
容器中另一个bean
的beanName
时,我们还可以通过idref
元素来指定对应的值,此时对应的值是通过idref
元素的bean
属性来指定的。当使用idref
元素来指定对应的值时Spring
将检查ApplicationContext
中是否存在id
或name
为idref
元素的bean
属性值的bean
,所以当我们使用idref
元素时需要我们的ApplicationContext
中存在idref
对应的bean
。当我们需要指定的值确实是ApplicationContext
中一个bean
的id
或name
时,这可以帮助我们在Spring
初始化对应的bean
时就发现对应的错误。
<bean id="world" class="com.app.World"/>
<bean id="hello" class="com.app.Hello">
<!-- 将字符串world通过bean的setP1()方法进行注入 -->
<property name="p1">
<!-- 注入的是字符串world,但是Spring将检查ApplicationContext中是否存在id或name为world的bean -->
<idref bean="world"/>
</property>
</bean>
在上述配置中我们给id
为hello
的bean
通过setP1()
方法注入的是字符串world
,而不是其对应的bean
。由于我们是通过idref
元素来注入字符串world
的,Spring
将检查ApplicationContext
中是否存在id
或name
为world
的bean
。
3.4ref元素关联其它bean
不管是通过构造方法注入还是通过set
方法注入依赖项,我们都可以通过ref
元素关联其它的bean
。ref
元素可以用来定义对应的关联项,而真正的关联项是通过ref
元素的bean
属性或parent
属性来指定的,它们对应的是目标bean
的id
或name
。
<bean id="hello" class="com.app.Hello">
<property name="world">
<ref bean="world"/><!-- 关联id或name为world的bean -->
</property>
</bean>
<bean id="world" class="com.app.World"/>
ref
的bean
属性关联和parent
属性关联是不同的。通过bean
属性指定关联时会在当前容器及其父容器中寻找关联项,而通过parent
属性指定关联时只会在当前容器的父容器中寻找关联项。
<!-- 假设是定义在父容器中的bean -->
<bean id="parenWorld" class="com.app.World"/>
<bean id="hello" class="com.app.Hello">
<property name="world">
<ref parent="parentWorld"/><!-- 关联父容器中id或name为world的bean -->
</property>
</bean>
不管是通过构造方法注入还是通过set
方法注入,我们都可以通过属性ref
来替代<ref bean=”…”/>
,如上面的示例我们就可以替换成如下这样:
<bean id="hello" class="com.app.Hello">
<property name="world" ref="world"/><!-- 使用ref属性替代<ref bean="..."/> -->
</bean>
<bean id="world" class="com.app.World"/>
3.5复合属性的注入
设想一下我们的beanA
有一个属性beanB
,而beanB
又有一个属性beanC
,beanC
又有一个属性propD
,我们可以直接在定义beanA
的时候指定对应的beanB
的属性beanC
的属性propD
的值,如:
<bean id="beanA" class="com.app.beanA">
<property name="beanB.beanC.propD" value="ValueD"/>
</bean>
其对应的语义是beanA.getBeanB().getBeanC().setPropD(“ValueD”)
,所以能够成功指定propD
值的条件是getBeanB()
不能为null
,getBeanB()
的getBeanC()
也不能为null
。当然,最终要注入的不一定是基本数据类型,换成是一个bean
也是可以的,如:
<bean id="beanA" class="com.app.beanA">
<property name="beanB.beanC.beanD" ref="beanD"/>
</bean>
(注:本文是基于Spring4.1.0所写)