注入依赖
依赖注入(DI)的基本原理是对象之间的依赖,通过以下几种方式实现:
构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。
容器的工作就是创建bean时注入那些依赖关系,这样相对由bean自己来完成这些工作从控制上发生了根本性的倒转,因此依赖注入也叫控制反转(IoC)。
DI注入有Setter和构造器两种注入方式。
构造器注入:
通过带参数的构造器实现,每个参数就是一个依赖。也可以通过静态工厂方法来传参数,但两者是类似的,
先来看下只使用构造器参数来注入依赖关系的例子:
public class SimpleMovieLister {
//依赖于MovieFinder
private MovieFinder movieFinder;
//注入到构造器中
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
构造器参数解析:
根据参数类型来匹配,若参数类型定义明确,则bean实例化时根据参数定义的顺序来匹配,见下例:
package x.y;
public class Foo {
//构造器的参数类型分别是Bar和Baz
public Foo(Bar bar, Baz baz) {
}
}
看下其配置:
<beans> <bean name=”foo” class=”x.y.Foo”> <constructor-arg> <bean class=”x.y.Bar”/> </constructor-arg> <constructor-arg> <bean class=”x.y.Baz”/> </constructor-arg> </bean> </beans>
再看下有简单类型的情况:
package examples;
public class ExampleBean {
//简单类型虽然明确,但在配置时必须指明type才能匹配
private int years;
// 同上
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
看下其配置情况:
<beans> <bean id=”exampleBean” class=”examples.ExampleBean”> <!---指明类型-à <constructor-arg type=”int” value=”7500”/> <constructor-arg type=”java.lang.String” value=”42”/> </bean> </beans>
也可以用索引的方式达上与上面同样的效果:
<beans>
<bean id=”exampleBean” class=”examples.ExampleBean”>
<!---索引从0开始-à
<constructor-arg index=”0” value=”7500”/>
<constructor-arg index=”1” value=”42”/>
</bean>
</beans>
Setter注入:
通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI。如:
public class SimpleMovieLister { //依赖MovieFinder private MovieFinder movieFinder; //用setter方法注入 public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
构造器注入与setter注入的选择?
根据具体情况使用,一般而言,构造器注入不宜依赖参数过多的情况,否则构造器显得比较臃肿,参数类型或名字以及顺序也容易写错。Setter注入当依赖对象过多的时候可以考虑,缺点就是代码也可能臃肿。
处理Bean依赖关系的步骤:
a) 根据bean配置文件创建并初始化BeanFactory实例,一般使用ApplicationContext实现。
b) Bean被创建的时候,依赖的属性、构造器参数或静态工厂方法参数都要提供给此bean。
c) 依赖的属性或构造器参数可以是一个值也可以是个引用。
d) 指定的属性或构造器参数值必须能转换成特定的格式或构造器参数所需的类型。
Spring容器在创建的时候会对每个配置的bean进行验证,检查bean指定的属性引用是否有效。在bean实际被创建之前,依赖的属性是不会被设置的,对于单例或被设置提前实例化的bean将与容器同时创建。其它情况的bean会被延迟到使用时才创建。
默认情况下,ApplicationContext实现中的bean采用提前实例化的singleton模式,这样做的好处是可以提前进行配置检查。被依赖的bean必须在依赖bean创建之前配置好。
一些例子:
Setter注入的配置:
public class ExampleBean {
//依赖的属性
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
//用setter方法注入
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
public class ExampleBean {
//依赖的属性
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
//用setter方法注入
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
构造器注入的配置:
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">
<!—用constructor-arg元素来配置,此处指向另一个引用的bean -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!—同上-->
<constructor-arg ref="yetAnotherBean"/>
<!—简单类型,要写type属性-->
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { // 私有的构造器 private ExampleBean(...) { ... } //静态工厂方法来实例化(参数为依赖的对象) public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); return eb; } }
<bean id="exampleBean" class="examples.ExampleBean"
factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
请注意,传给static工厂方法的参数由constructor-arg元素提供,这与使用构造器注入时完全一样。而且,重要的是,工厂方法所返回的实例的类型并不一定要与包含static工厂方法的类类型一致。尽管在此例子中它的确是这样。非静态的实例工厂方法与此相同(除了使用factory-bean属性替代class属性外)。
实例工厂方法参数注入:
public class ExampleBean { // 私有的构造器 private ExampleBean(...) { ... } //实例工厂方法来实例化(参数为依赖的对象) public ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); return eb; } }
<bean id="instanceBean" factory-bean=”exampleBean” factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id=”exampleBean” class="examples.ExampleBean"/> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>