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属性,用于确定该属性值的数据类型。