7.3.3 构造注入
7.3.3 构造注入
前面已经介绍过,通过setter
方法为目标Bean
注入依赖关系的方式被称为设值注入;另外还有种注入方式,这种方式在构造实例时,已经为其完成了依赖关系的初始化。这种利**用构造器来设置依赖关系的方式,被称为构造注入
**。
构造注入的本质
通俗来说,就是驱动Spring
在底层以反射方式执行带参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化—这就是构造注入的本质。
现在问题产生了:<bean>
元素默认总是驱动Spring
调用无参数的构造器来创建对象,那怎样驱动Spring
调用有参数的构造器去创建对象呢?答案是使用<constructor-arg>
子元素,每个<constructor-arg>
子元素代表一个构造器参数,如果<bean>
元素包含N
个<constructor-arg>
子元素,就会驱动Spring
调用带N
个参数的构造器来创建对象.
程序示例
对前面代码中的Chinese
类做简单的修改,修改后的代码如下。
Chinese.java
1 | package org.crazyit.app.service.impl; |
上面的Chinese
类没有提供设置axe
成员变量的setter
法,仅仅提供了一个带Axe
参数的构造器。Spring
将通过该构造器为chinese
注入所依赖的Bean
实例。
构造注入的配置文件也需做简单的修改,为了使用构造注入(也就是驱动Spring
调用有参数的构造器创建对象),还需使用<constructor-arg>
元素指定构造器的参数。修改后的配置文件如下。
beans.xml
1 |
|
上面配置文件中的粗体字代码使用<constructor-arg>
元素指定了一个构造器参数,该参数类型是Axe
,这指定Spring
调用Chinese
类里带一个Axe
参数的构造器来创建chinese
实例。也就是说,上面代码相当于驱动Spring
执行如下代码:
1 | String idStr=...;//Spring解析<bean>元素得到id属性值为chinese |
从上面字代码可以看出,由于使用了有参数的构造器创建实例,所以当Bean
实例被创建完成后,该Bean
的依赖关系已经设置完成。
该示例的执行效果与设值注入steelAxe
时的执行效果完全一样。区别在于:
- 创建
Person
实例中Axe
属性的时机不同——设值注入是先通过无参数的构造器创建一个Bean
实例,然后调用对应的setter
方法注入依赖关系; - 而构造注入则直接调用有参数的构造器,当
Bean
实例创建完成后,已经完成了依赖关系的注入
index属性
配置<constructor-arg>
元素时可指定一个index
属性,用于指定该构造参数值将作为第几个构造参数值,例如,指定index="0"
表明该构造参数值将作为第一个构造参数值。
希望Spring
调用带几个参数的构造器,就在<bean>
元素中配置几个<constructor-arg>
子元素。例如如下配置代码:
1 | <!--定义名为bean1的Bean,对应的实现类为lee.Test1--> |
上面的代码相当于让Spring
调用如下代码( Spring
底层用反射执行该代码)
1 | Object bean1=new lee.Test1("hello","23");//①号代码 |
由于Spring
本身提供了功能强大的类型转换机制,因此如果lee.Test1
只包含一个Test(String,int)
构造器,那么上面的配置片段相当于让Spring
执行如下代码( Spring
底层用反射执行该代码):
1 | Object bean1=new lee.Test1("hello",23);//②号代码 |
这就产生一个问题:如果lee.Test1
类既有Testl(String, String)
构造器,又有Test1(String,int)
构造器,那么上面的粗体字配置片段到底让Spring
执行哪行代码呢?答案是①号代码,因为此时的配置还不够明确:对于<constructor-arg value="23>
,Spring
只能解析出一个"23"
字符串,但它到底需要转换为哪种数据类型——从配置文件中看不出来,只能根据lee.Test1
的构造器来尝试转换.
type属性
为了更明确地指定数据类型, Spring
允许为<constructor-arg.>
元素指定一个type
属性,例如<constructor-arg value="23" type="int">
,此处Spring
明确知道此处配置了一个int
类型的参数。与此类似的是,<value>
元素也可指定type
属性,用于确定该属性值的数据类型。