BeanFactory的XML之旅

bean的互相依赖

4. depends-on

通常情况下,可以直接通过之前提到的所有元素,来显式地指定bean之间的依赖关系。这样,容器在初始化当前bean定义的时候,会根据这些元素所标记的依赖关系,首先实例化当前bean定义所依赖的其他bean定义。但是,如果某些时候,我们没有通过类似<ref>的元素明确指定对象A依赖于对象B的话,如何让容器在实例化对象A之前首先实例化对象B呢?

考虑以下所示代码:

  
    public class SystemConfigurationSetup
    {
    	static
    	{
    	// 其他初始化代码 
      }
    	...
    }
    

系统中所有需要日志记录的类,都需要在这些类使用之前首先初始化log4j。那么,就会非显式地依赖于SystemConfigurationSetup的静态初始化块。如果ClassA需要使用log4j,那么就必须在bean定义中使用depends-on来要求容器在初始化自身实例之前首先实例化SystemConfigurationSetup,以保证日志系统的可用,如下代码演示的正是这种情况:

  
    <bean id="classAInstance" class="...ClassA" depends-on="configSetup"/>
    <bean id="configSetup" class="SystemConfigurationSetup"/>
    

举log4j在静态代码块(staticblock)中初始化的例子在实际系统中其实不是很合适,因为通常在应用程序的主入口类初始化日志就可以了。这里主要是给出depends-on可能的使用场景,大部分情况下, 是那些拥有静态代码块初始化代码或者数据库驱动注册之类的场景

如果说ClassA拥有多个类似的非显式依赖关系,那么,你可以在ClassA的depends-on中通过逗号分割各个beanName。

5. autowire

除了可以通过配置明确指定bean之间的依赖关系,Spirng还提供了根据bean定义的某些特点将相互依赖的某些bean直接自动绑定的功能。通过<bean>的autowire属性,可以指定当前bean定义采用某种类型的自动绑定模式。这样,你就无需手工明确指定该bean定义相关的依赖关系,从而也可以免去一些手工输入的工作量。

Spring提供了5种自动绑定模式,即no、byName、byType、constructor和autodetect,下面是它们的具体介绍。

  • no 容器默认的自动绑定模式,也就是不采用任何形式的自动绑定,完全依赖手工明确配置各个bean之间的依赖关系。

  • byName

按照类中声明的实例变量的名称,与XML配置文件中声明的bean定义的beanName的值进行匹配,相匹配的bean定义将被自动绑定到当前实例变量上。这种方式对类定义和配置的bean定义有一定的限制。假设我们有如下所示的类定义:

 
        public class Foo {
    private Bar emphasisAttribute; 
      ...
    	// 相应的setter方法定义
    }
    public class Bar {
    	...
    }
    

那么应该使用如下代码所演示的自动绑定定义,才能达到预期的目的:

    
        <bean id="fooBean" class="...Foo" autowire="byName">
    </bean>
    
    <bean id="emphasisAttribute" class="...Bar">
    </bean>
    

需要注意两点:第一,我们并没有明确指定fooBean的依赖关系,而仅指定了它的autowire属性为byName;第二,第二个bean定义的id为emphasisAttribute,与Foo类中的实例变量名称相同

  • byType

如果指定当前bean定义的autowire模式为byType,那么,容器会根据当前bean定义类型,分析其相应的依赖对象类型,然后到容器所管理的所有bean定义中寻找与依赖对象类型相同的bean定义,然后将找到的符合条件的bean自动绑定到当前bean定义。

对于byName模式中的实例类Foo来说,容器会在其所管理的所有bean定义中寻找类型为Bar的bean定义。如果找到,则将找到的bean绑定到Foo的bean定义;如果没有找到,则不做设置。但如果找到多个,容器会告诉你它解决不了“该选用哪一个”的问题,你只好自己查找原因,并自己修正该问题。所以, byType只能保证,在容器中只存在一个符合条件的依赖对象的时候才会发挥最大的作用 ,如果容器中存在多个相同类型的bean定义,那么,不好意思,采用手动明确配置吧!

    
    <bean id="fooBean" class="...Foo" autowire="byType">
    </bean>
    
    <bean id="anyName" class="...Bar">
    </bean>
    
  • constructor

byName和byType类型的自动绑定模式是针对property的自动绑定,而constructor类型则是针对 构造方法参数的类型而进行的自动绑定,它同样是byType类型的绑定模式。不过,constructor是匹配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的bean定义,那么,容器会返回错误。使用上也与byType没有太大差别,只不过是应用到需要使用构造方法注入的bean定义之上,下方代码给出了一个使用construtor模式进行自动绑定的简单场景演示。

    
    public class Foo {
    	private Bar bar;
    	public Foo(Bar arg)
    	{
    		this.bar = arg;
    	} ...
    }
    

相应配置为

    <bean id="foo" class="...Foo" autowire="constructor"/>
    
    <bean id="bar" class="...Bar">
    </bean>
  • autodetect

这种模式是byType和constructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式。否则,会使用constructor模式。当然,如果通过构造方法注入绑定后还有其他属性没有绑定,容器也会使用byType对剩余的对象属性进行自动绑定。

手工明确指定的绑定关系总会覆盖自动绑定模式的行为

自动绑定只应用于“原生类型、String类型以及Classes类型以外”的对象类型,对“原生类型、String类型和Classes类型”以及“这些类型的数组”应用自动绑定是无效的。

作为所有<bean>的统帅,<beans>有一个default-autowire属性,它可以帮我们省去为多个<bean>单独设置autowire属性的麻烦,default-autowire的默认值为no,即不进行自动绑定。如果想让系统中所有的<bean>定义都使用byType模式的自动绑定,我们可以使用如下配置内容:

    
    <beans default-autowire="byType">
      <bean id="..." class="..."/>
      ...
    </beans>
    

6. dependency-check

我们可以使用每个<bean>dependency-check属性对其所依赖的对象进行最终检查,该功能主要与自动绑定结合使用,可以保证当自动绑定完成后,最终确认每个对象所依赖的对象是否按照所预期的那样被注入。

可以通过dependency-check指定容器帮我们检查某种类型的依赖,基本上有如下4种类型的依赖检查。

  • none。不做依赖检查。
  • simple。对简单属性类型以及相 关的collection进行依赖检查,对象引用类型的依赖除外。
  • object。只对对象引用类型依赖进行检查。
  • all。将simple和object相结合,也就是说会对简单属性类型以及相应的collection和所有对象引用类型的依赖进行检查。

总地来说,控制得力的话,这个依赖检查的功能我们基本可以不考虑使用。

7. lazy-init

延迟初始化(lazy-init)这个特性的作用,主要是可以针对ApplicationContext容器的bean初始化行为施以更多控制。如果我们想改变某个或者某些bean定义在ApplicationContext容器中的默认实例化时机。这时,就可以通过<bean>的lazy-init属性来控制这种初始化行为,如下代码所示:

    
    <bean id="lazy-init-bean" class="..." lazy-init="true"/>
    <bean id="not-lazy-init-bean" class="..."/>
    

这样,ApplicationContext容器在启动的时候,只会默认实例化其他没有这么设置的bean而不会实例化已经设置了lazy-init的bean了。

当然,仅指定lazy-init-bean的lazy-init为true,并不意味着容器就一定会延迟初始的顺序,如果某个非延迟初始化的bean定义依赖于lazy-init-bean ,那么毫无疑问,按照依赖解决的顺序,容器还是会首先实例化lazy-init-bean,然后再实例化后者,如下代码演示了这种相互牵连导致延迟初始化失败的情况:

 
    
    <bean id="lazy-init-bean" class="..." lazy-init="true"/>
    
    <bean id="not-lazy-init-bean" class="...">
      <property name="propName">
    	<ref bean="lazy-init-bean"/>
      </property>
    </bean>
    

虽然lazy-init-bean是延迟初始化的,但因为依赖它的not-lazy-init-bean并不是延迟初始化,所以lazy-init-bean还是会被提前初始化,延迟初始化的良好打算“泡汤”。如果我们真想保证lazy-init-bean一定会被延迟初始化的话,就需要保证依赖于该bean定义的其他bean定义也同样设置为延迟初始化

在bean定义很多时,好像工作量也不小哦。不过不要忘了,<beans>可是所有<bean>的统领啊,让它一声令下吧!如下方代码所演示的,在顶层由<beans>统一控制延迟初始化行为即可。

    <beans default-lazy-init="true">
        <bean id="lazy-init-bean" class="..." />
      
        <bean id="not-lazy-init-bean" class="...">
            <property name="propName">
                <ref bean="lazy-init-bean" />
            </property>
        </bean>
        ...
    </beans>