依赖注入 (DI) 是指定义对象之间的依赖关系的过程。容器的工作就是创建bean并注入依赖关系。这个过程来由容器来控制bean的实例或者其依赖关系,因此它有另外一个名字叫控制反转 (IoC)。
DI主要有两种注入方式,即构造器注入和Setter注入。
构造器注入
基于构造器注入DI通过调用带参数的构造器来实现,每个参数代表着一个依赖关系。
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<!-- 使用嵌套的ref元素 -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- 使用直接的ref属性 -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
bean被实例化的时候,默认按bean定义中构造器参数的定义顺序依次进行匹配。
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
可以使用type属性来显式指定简单类型的构造参数类型:
package examples;
public class ExampleBean {
private int years;
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
也可以使用index属性来显式指定构造参数的索引:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
还可以使用构造器参数命名来指定:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
不过这种方式需要使用@ConstructorProperties JDK注解明确指出构造函数的参数:
package examples;
public class ExampleBean {
// 这里属性定义省略掉了
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
Setter注入
容器通过回调bean的setter方法来完成setter注入。
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<!-- 使用嵌套的ref元素 -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- 使用直接的ref属性 -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
构造器注入和Setter注入如何选择
因为可以混合使用构造器注入和setter注入,强制性依赖关系时使用构造器注入,可选的依赖关系时使用setter方法注入是比较好的经验法则。
当实现的应用组件是不可变对象时使用构造器注入,构造器注入的组件总是返回给调用者完整的初始化状态。
setter注入主要用作可选的依赖,给这些依赖分配合理的缺省值。否则,当代码使用依赖时必须进行非空检查。setter注入的一个好处是setter方法使得这个类的对象在以后的某个时候还可合理的重新配置或者重新注入。