7.5.3 配置依赖

7.5.3 配置依赖

根据前面的介绍,Java应用中各组件相互调用的实质可以归纳为依赖关系,根据注入方式的不同,Bean的依赖注入通常有如下两种形式。

  1. 设值注入:通过<property>元素驱动Spring执行setter方法。
  2. 构造注入:通过<constructor-arg>元素驱动Spring执行带参数的构造器。

不管是设值注入,还是构造注入,都视为Bean的依赖,接受Spring容器管理,依赖关系的值要么是一个确定的值,要么是Spring容器中其他Bean的引用
通常不建议使用配置文件管理Bean的基本类型的属性值;通常只使用配置文件管理容器中BeanBean之间的依赖关系。
对于singleton作用域的Bean,如果没有强行取消其预初始化行为,系统会在创建Spring容器时预初始化所有的singleton Bean,与此同时,该Bean所依赖的Bean也被一起实例化。
BeanFactoryApplicationContext实例化容器中Bean的时机不同:前者等到程序需要Bean实例时才创建Bean:而后者在容器创建Application Context实例时,会预初始化容器中所有的singleton Bean

因为采用ApplicationContext作为Spring容器,创建容器时会同时创建容器中所有singleton作用域的Bean,因此可能需要更多的系统开销。但一旦创建成功,应用后面的响应速度更快,因此,对于普通的Java EE应用,推荐使用ApplicationContext作为Spring容器.
创建BeanFactory时不会立即创建Bean实例,所以有可能程序可以正确地创建BeanFactory实例,但当请求Bean实例时依然抛出一个异常:创建Bean实例或注入它的依赖关系时出现错误。
配置错误的延迟出现,也会给系统引入不安全因素,而ApplicationContext则默认预实例化所有singleton作用域的Bean,所以ApplicationContext实例化过程比BeanFactory实例化过程的时间和内存开销大,但可以在容器初始化阶段就检验出配置错误。
除此之外, Spring可以为任何Java对象注入任何类型的属性—只要该Java对象为该属性提供了应的setter方法即可。
例如,如下配置片段:

1
2
3
4
<bean id="id" class="lee.AClass">
<property name="aaa" value="aValue">
<property name="bbb" value="bValue">
</bean>

对于上面的配置片段,有效的数据只是那些粗体字内容, Spring将会为每个<bean>元素创建一个Java对象—这个Java对象就是一个Bean实例。对于上面的程序, Spring将采用类似于如下的代码创建Java实例。

1
2
3
4
// 获取lee.AClass类的Class对象
Class targetClass=Class.forName("lee.AClass");
// 调用lee.AClass类的无参构造器创建对象
Object bean=targetClass.newInstance();

创建该实例后, Spring接着遍历该<bean>元素里所有的<property>子元素,<bean>元素每包含一个<property>子元素, Spring就为该bean实例调用一次setter方法。对于上面第一行<property>子元素,将有类似的如下代码:

1
2
3
4
5
// 获取第一个<property>元素的name属性值对应的 setter方法名
String setName1="set"+"Aaa";
// 获取lee.Class类里的setAaa()方法
Method setMethod1=targetClass.getMethod(setName1,aValue.getClass());
setMethod1.invoke(bean,aValue);

通过类似上面的代码, Spring就可根据配置文件的信息来创建Java实例,并调用该Java实例的setter方法(这就是所谓的设值注入)
对于如下配置片段:

1
2
3
4
<bean id="id" class="lee.AClass">
<constructor-arg index="1" value="aValue"/>
<constructor-arg index="0" value="bValue"/>
</bean>

上面的配置片段指定了两个<constructor-arg/>子元素, Spring就不再采用默认的构造器来创建Bean实例,而是使用特定构造器来创建该Bean实例。
Spring将会采用类似如下的代码来创建Bean实例:

1
2
3
4
5
6
// 获取lee.AClass类的Class对象
Class targetClass=Class.forName("lee.AClass");
// 获取第一个参数是bValue类型,第二个参数是aValue类型的构造器
Constructor targetCtr=targetClass.getConstructor(bValue.getClass(),aValue.getClass());
// 以指定构造器创建Bean实例
Object bean=targetCtr.newInstance(bValue,aValue);

上面的程序片段仅是一个示例, Spring实际上还需要根据<property>元素、<contructor-arg>元素所使用的value属性、ref属性等来判断需要注入的到底是什么数据类型,并要对这些值进行合适的类型转换,所以Spring实际的处理过程更复杂。
由此可见,
构造注入就是通过<constructor-arg>驱动Spring执行有参数的构造器;
设值注入就是通过<property>.驱动Spring执行setter方法。
不管哪种注入,都需要为参数传入参数值,而Java类的成员变量可以是各种数据类型,除了基本类型值、字符串类型值等,还可以是其他Java实例,也可以是容器中的其他Bean实例,甚至是Java集合、数组等,所以Spring允许通过如下元素为setter方法、构造器的参数指定参数值。

  1. value
  2. ref
  3. bean
  4. list,set,map,props
    上面4种情况分别代表Bean类的4种类型的成员变量,下面详细介绍这4种情况。