Elim的博客

用来记录一些原创性的总结


Elim

3依赖注入

Spring bean的依赖注入有两种形式,通过构造方法注入和通过set方法注入。构造方法注入是通过constructor-arg元素来指定的,而set方法注入是通过property元素来指定的。

3.1构造方法注入

先来看一个简单的示例:

	<bean id="hello" class="com.app.Hello">
		<constructor-arg value="hello"/>
	</bean>

上述配置定义了一个类型为com.app.Hellobean,我们通过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容器中另一个beanbeanName时,我们还可以通过idref元素来指定对应的值,此时对应的值是通过idref元素的bean属性来指定的。当使用idref元素来指定对应的值时Spring将检查ApplicationContext中是否存在idnameidref元素的bean属性值的bean,所以当我们使用idref元素时需要我们的ApplicationContext中存在idref对应的bean。当我们需要指定的值确实是ApplicationContext中一个beanidname时,这可以帮助我们在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>

在上述配置中我们给idhellobean通过setP1()方法注入的是字符串world,而不是其对应的bean。由于我们是通过idref元素来注入字符串world的,Spring将检查ApplicationContext中是否存在idnameworldbean

3.4ref元素关联其它bean

不管是通过构造方法注入还是通过set方法注入依赖项,我们都可以通过ref元素关联其它的beanref元素可以用来定义对应的关联项,而真正的关联项是通过ref元素的bean属性或parent属性来指定的,它们对应的是目标beanidname

	<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"/>

refbean属性关联和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又有一个属性beanCbeanC又有一个属性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()不能为nullgetBeanB()getBeanC()也不能为null。当然,最终要注入的不一定是基本数据类型,换成是一个bean也是可以的,如:

	<bean id="beanA" class="com.app.beanA">
		<property name="beanB.beanC.beanD" ref="beanD"/>
	</bean>

(注:本文是基于Spring4.1.0所写)