容器按照如下过程执行依赖解析过程。
- 创建
ApplicationContext
并使用配置元数据(描述了所有的bean)初始化。配置元数据可以用XML,Java代码或者注解指定。 - 对于每个bean,它的依赖可以用属性的表单,构造器参数,或者静态工厂方法参数表示,如果你使用那个代替一个普通的构造器。当那个bean真正被创建时,这些依赖提供给这个bean
- 每个属性或者构造器参数是一个要设置值的一个实际定义,或者一个容器内另一个bean的引用。
- 每个属性或者构造器参数(是一个值),从其指定格式转换为那个属性或者构造器参数的实际类型。默认地,Spring可以转换一个String类型的值成所有内置的类型,比如int,long,string,boolean等等
Spring容器校验每个bean的配置,当容器创建时。然而,bean属性自身没有设置直到那个bean真正的创建了。这些是singleton-scope并且默认设置为预实例化的bean是随着创建了容器时才创建。否则,那些bean只能在其需要的时候才创建。一个bean的创建潜在的会创建更多的bean,还有bean的依赖和它的依赖的依赖创建和分配。注意那些依赖的解析不匹配可能很晚才出现,会影响第一个bean的创建。
循环依赖
如果你主要使用构造器注入,可能会导致一个不能分辨的循环依赖情况。
例如:Class A实例通过构造器注入要求一个Class B的实例,而Class B的实例通过构造器注入需要一个Class A的实例。如果你为Class A和Class B配置bean为彼此互相注入,Spring IoC容器在运行时会发现循环依赖并抛出一个BeanCurrentlyInCreationException
异常。
一个可能的解决方案是修改要配置的某些类的源代码,通过setter注入而不是构造器注入。也要避免仅仅使用构造器注入和setter注入。换句话说,虽然不提倡,你可以使用setter注入配置循环依赖。
不像典型情况(没有循环依赖),Bean A和Bean B之间的循环依赖强制beans之一的注入到另一个之中。在自身完全初始化之前(一个典型的鸡生蛋问题)
你一般的可以信任Spring做那正确饿事情。它发现配置问题,比如在容器加载期间,不存在的bean的引用和循环依赖。Spring会尽可能晚的设置属性并解决依赖,当那个bean真正创建的时候。这就意味着当前正在加载的Spring容器能晚点产生一个异常,当你请求一个对象,如果创建那个对象或者一个对象的引用时出现了问题。例如,由于属性确实或者无效那个bean抛出了一个异常。这个潜在的延迟了一些配置问题的可见性就是为什么
在这些bean真正需要时花些时间和内存创建它们,你会在创建ApplicationContext
默认地由预实例化的单例bean实现。ApplicationContext
时发现配置问题,不会很晚。你仍旧可以重写默认的方法,这样单例bean将延迟初始化而不是预先实例化。
如果没有循环依赖存在,当一个或者更多协作的beans注入到一个单独的bean中时,每个协作的bean将在注入到独立的bean中之前完全地配置。这意味着,如果bean A有一个bean b的依赖,Spring IoC 容器将在调用bean A的setter方法前完成配置bean B。换句话说,实例化bean(如果不是一个预先实例化的单例),其依赖也将配置,并且调用的相关的生命周期方法。(比如init方法或者callback方法)
依赖注入的例子
下面的例子演示了基于XML配置元数据实现setter 依赖注入。Spring XML配置的一小部分指定了一些bean的定义:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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;
}
}
在前面的例子中,setters声明为匹配XML文件中指定的属性。下面的例子是用构造器依赖注入:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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定义中指定的构造器参数对应于ExampleBean的构造器的参数。
现在考虑这个例子的变本,不使用构造器,告诉Spring调用一个static工厂方法返回那个对象的实例。
<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"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
静态工厂方法的参数由
<constructor-arg/>
元素提供,与基于构造器注入的标签相同。由工厂方法返回的类类型不必与包含了静态工厂方法返回的类类型相同,虽然这里的例子是这样的。一个实例(非静态)的工厂方法将以一个完全相同的方式使用(除了factory-bean属性的使用代替class属性),所以这里不讨论细节。