Spring IoC容器详解(二)

依赖的处理过程

容器解决依赖的过程

  • ApplicationContext通过配置元数据去创建和初始化,配置元数据描述了所有的bean,配置元数据可以是XML,java代码,注解
  • 对于每个bean,它的依赖关系以属性,构造函数参数或静态工厂方法(如果不是用正常的构造函数)的参数的形式表示,当实际创建bean的时候,它的依赖被提供
  • 每个属性或者构造函数参数被设置一个实际值或引用bean容器里的其他bean
  • 每个属性的值或者构造函数参数的值根据描述信息转化成属性或者构造函数参数实际的类型.默认情况下,Spring可以把一个字符串格式的值转换成所有的内置的类型,如int,long,String,boolen等.
    Spring容器在容器创建时验证每个bean的配置.但是,在实际创建 bean之前,bean属性本身不会被设置.当容器创建的时候单例的bean被预先实例化(默认情况下).作用域定义在 Bean 作用域.否则,只有在请求时才创建bean.创建一个bean可能会导致一系列的bean被创建,因为bean的依赖关系及其依赖关系的依赖关系(等等)被创建和分配.注意到依赖不匹配的处理晚点会说明.
循环依赖

如果你主要使用构造函数方式注入,你可能会碰到一个无法解析的循环依赖场景,比如类A需要类B的实例,通过构造函数注入.类B需要类A的实例,也是通过构造函数注入.如果将类A和B的Bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并引发一个 BeanCurrentlyInCreationException

解决的办法时修改代码,配置成setter方法注入而不是通过构造函数注入,或者避免构造函数注入而仅使用setter方法注入,简单来说就是用setter方法注入来配置一个循环依赖

与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖关系迫使其中一个bean在被完全初始化之前注入另一个bean(经典的鸡/鸡蛋场景)

你可以相信Spring能够正确的处理事情.它在容器加载的时候会检测配置问题,比如引用不存在的bean,循环依赖.Spring创建bean的时候,尽可能迟的设置属性值和解决依赖关系.这意味着,一个正确加载的spring容器在请求一个对象时也会产生异常,如果创建这个对象或它的依赖时出现问题.比如,当属性值缺失或非法,bean会抛出异常.某些配置问题被发现会延迟是因为ApplicationContext的默认实现会预先实例化单例bean.在实际需要这些bean之前,为了创建这些bean需要一定的时间和内存,您会在ApplicationContext创建时发现配置问题,而不是之后.你可以覆盖默认的实现,这样单例的bean会延迟实例化不是预先实例化
  如果不存在循环依赖,一个或多个协作bean被注入到一个依赖bean之前都是完全配置.这意味着,假如有个Bean A它依赖Bean B,容器调用Bean A的setter方法之前已经完全配置好Bean B.换句话说,Bean被初始化,设置它的依赖,相关的生命周期方法会被调用.

依赖注入例子

下面是基于setter方法的依赖注入例子

<bean id="exampleBean" class="example.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定义里描述的constructor-arg会被当作ExampleBean构造函数的参数
替换使用构造函数,使用静态工厂方法返回一个实例对象

<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"/>
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;
}
}

给静态工厂方法提供参数通过元素,就像实际使用构造函数一样.静态工厂方法返回的类型,不是必须与静态工厂方法所在的类一样,尽管我们的例子是这样子的.实例(非静态)工厂方法将以基本相同的方式使用(除了使用factory-bean属性而不是class属性之外),因此在此不讨论细节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值