Spring重温(三)--Spring依赖注入(DI)
前言:在Spring框架中,DI(依赖注入)是用来定义对象彼此间的依赖,主要有set方法注入和构造器注入两种方式。另外,当一个类包含多个构造函数带的参数相同,它总是会造成构造函数注入参数类型歧义的问题,我会在第3点进行介绍并给出解决方案。
1.setter方法注入:
package com.yiibai.output; import com.yiibai.output.IOutputGenerator; public class OutputHelper { IOutputGenerator outputGenerator; public void setOutputGenerator(IOutputGenerator outputGenerator){ this.outputGenerator = outputGenerator; } }
一个 bean 配置文件用来声明bean 和通过 setter 设置注入(property标签)的依赖。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="OutputHelper" class="com.yiibai.output.OutputHelper"> <property name="outputGenerator"> <ref bean="CsvOutputGenerator" /> </property> </bean> <bean id="CsvOutputGenerator" class="com.yiibai.output.impl.CsvOutputGenerator" /> </beans>
2.构造器注入:
例子:
package com.yiibai.output; import com.yiibai.output.IOutputGenerator; public class OutputHelper { IOutputGenerator outputGenerator; OutputHelper(IOutputGenerator outputGenerator){ this.outputGenerator = outputGenerator; } }
bean 配置文件来声明bean并通过构造函数(constructor-arg标签)设置注入依赖
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="OutputHelper" class="com.yiibai.output.OutputHelper"> <constructor-arg> <bean class="com.yiibai.output.impl.CsvOutputGenerator" /> </constructor-arg> </bean> <bean id="CsvOutputGenerator" class="com.yiibai.output.impl.CsvOutputGenerator" /> </beans>
3.当一个类包含多个构造函数带的参数相同,它总是会造成构造函数注入参数类型歧义的问题。
让我们来看看这个客户 bean 实例。它包含两个构造方法,均接受3个不同的数据类型参数:
package com.yiibai.common; public class Customer { private String name; private String address; private int age; public Customer(String name, String address, int age) { this.name = name; this.address = address; this.age = age; } public Customer(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } //getter and setter methods public String toString(){ return " name : " +name + "\n address : " + address + "\n age : " + age; } }
在Spring bean 的配置文件中,我们传递一个“yiibai' 的名字,地址为'188',以及年龄为'28'。
<!--Spring-Customer.xml--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="CustomerBean" class="com.yiibai.common.Customer"> <constructor-arg> <value>yiibai</value> </constructor-arg> <constructor-arg> <value>188</value> </constructor-arg> <constructor-arg> <value>28</value> </constructor-arg> </bean> </beans>
运行它,你期望的结果是什么?
package com.yiibai.common; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main( String[] args ) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"Spring-Customer.xml"}); Customer cust = (Customer)context.getBean("CustomerBean"); System.out.println(cust); } }
输出结果:
name : yiibai address : 28 age : 188
其结果不是我们所期望的,第一个构造器不执行,而是第二构造函数运行。在Spring参数类型'188' 能够转换成int,所以Spring只是转换它,并采用第二个构造来执行,即使你认为它应该是一个字符串。
另外,如果Spring不能解决使用哪个构造函数,它会提示以下错误信息
constructor arguments specified but no matching constructor found in bean 'CustomerBean' (hint: specify index and/or type arguments for simple parameters to avoid type ambiguities
为了解决这个问题,应该为构造函数指定的确切数据类型,通过像这样类型的属性:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="CustomerBean" class="com.yiibai.common.Customer"> <constructor-arg type="java.lang.String"> <value>yiibai</value> </constructor-arg> <constructor-arg type="java.lang.String"> <value>188</value> </constructor-arg> <constructor-arg type="int"> <value>28</value> </constructor-arg> </bean> </beans>
再次运行它,现在得到你所期望的:
name : yiibai address : 188 age
总结:在依赖注入中,主要有seter方法注入和构造函数注入两种方式,其中setter方法注入是在xml配置文件中通过property设置依赖,而构造器通过constructor-arg设置依赖;
显式声明每个构造函数参数的数据类型,可以避免上述构造注入型歧义的问题。