第4章 Spring的IoC容器之BeanFactory(二)
BeanFactory的XML之旅
<beans>
和<bean>
所有注册到容器的业务对象,在Spring中称之为Bean。所以,每一个对象在XML中的映射也自然 而然地对应一个叫做<bean>
的元素。既然容器最终可以管理所有的业务对象,那么在XML中把这些 叫做<bean>
的元素组织起来的,就叫做<beans>
。
1. <beans>
<beans>
是XML配置文件中最顶层的元素,它下面可以包含0或者1个<description>
和多个<bean>
以及<import>
或者<alias>
,如图4-4所示。

<beans>
作为所有<bean>
的“统帅”,它拥有相应的属性(attribute)对所辖的<bean>
进行统一的默认行为设置,包括如下几个。
- default-lazy-init。其值可以指定为true或者false,默认值为false。用来标志是否对所有的
<bean>
进行延迟初始化。 - default-autowire。可以取值为no、byName、byType、constructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用哪一种默认绑定方式。
- default-dependency-check。可以取值none、objects、simplze以及all,默认值为none,即不做依赖检查。
- default-init-method。如果所管辖的
<bean>
按照某种规则,都有同样名称的初始化方法的话,可以在这里统一指定这个初始化方法名,而不用在每一个<bean>
上都重复单独指定。 - default-destroy-method。与default-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法,可以通过这个属性统一指定。
当然,如果你不清楚上面这些默认的属性具体有什么用,那也不必着急。在看完对<bean>
的讲解之后,再回头来看,就会明了多了。给出这些信息,是想让你知道,如果在某个场景下需要对大部分<bean>
都重复设置某些行为的话,可以回头看一下,利用<beans>
是否可以减少这种不必要的工作。
2. <description>
、<import>
和<alias>
之所以把这几个元素放到一起讲解,是因为通常情况下它们不是必需的。
-
<description>
可以通过<description>
在配置的文件中指定一些描述性的信息。 -
<import>
可以通过<import>
元素对其所依赖的配置文件进行引用。 -
<alias>
可以通过<alias>
为某些<bean>
起一些“外号”(别名),通常情况下是为了减少输入。
<bean>
的介绍
每个业务对象作为个体,在Spring的XML配置文件中是与<bean>
元素一一对应的。如下代码演示了最基础的对象配置形式:
<bean id="djNewsListener" class="..impl.DowJonesNewsListener">
</bean>
id属性
通常,每个注册到容器的对象都需要一个唯一标志来将其与其他bean区分。通过id属性来指定当前注册对象的beanName
是什么。这里,通过id指定beanName为djNewsListener。实际上,并非任何情况下都需要指定每个<bean>
的id,有些情况下,id可以省略,比如后面会提到的内部<bean>
以及不需要根据beanName明确依赖关系的场合等。
除了可以使用id来指定<bean>
在容器中的标志,还可以使用name属性来指定<bean>
的别名 (alias)。比如,以上定义,我们还可以像如下代码这样,为其添加别名:
<bean id="djNewsListener"
name="/news/djNewsListener,dowJonesNewsListener"
class="..impl.DowJonesNewsListener">
</bean>
与id属性相比,name属性的灵活之处在于,name可以使用id不能使用的一些字符,比如/
。而且还可以通过逗号、空格或者冒号分割指定多个name。name的作用跟使用<alias>
为id指定多个别名基本相同:
<alias name="djNewsListener" alias="/news/djNewsListener"/>
<alias name="djNewsListener" alias="dowJonesNewsListener"/>
class属性
每个注册到容器的对象都需要通过<bean>
元素的class属性指定其类型,在大部分情况下,该属性是必须的。仅在少数情况下不需要指定,如后面将提到的在使用抽象配置模板的情况下。
bean的互相依赖
各个业务对象之间会相互协作来更好地完成同一使命。这时,各个业务对象之间的相互依赖就是无法避免的。对象之间需要相互协作,在横向上它们存在一定的依赖性。而现在我们就是要看一下,在Spring的IoC容器的XML配置中,应该如何表达这种依赖性。
既然业务对象现在都符合IoC的规则,那么要了解的表达方式其实也很简单,无非就是看一下构造方法注入
和setter方法注入
通过XML是如何表达的而已。那么,让我们开始吧!
1. 构造方法注入的XML之道
按照Spring的IoC容器配置格式,要通过构造方法注入方式,为当前业务对象注入其所依赖的对象, 需要使用<constructorarg>
。正常情况下,如以下代码所示:
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg>
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg>
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
对于<ref>
元素,稍后会进行详细说明。这里你只需要知道,通过这个元素来指明容器将为djNewsProvider这个<bean>
注入通过<ref>
所引用的Bean实例。这种方式可能看起来或者编写起来不是很简洁,最新版本的Spring也支持配置简写形式,如以下代码所示:
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg ref="djNewsListener"/>
<constructor-arg ref="djNewsPersister"/>
</bean>
有些时候,容器在加载XML配置的时候,因为某些原因,无法明确配置项与对象的构造方法参数列表的一一对应关系,就需要请<constructor-arg>
的type或者index属性出马。比如,对象存在多个构造方法,当参数列表数目相同而类型不同的时候,容器无法区分应该使用哪个构造方法来实例化对象,或者构造方法可能同时传入最少两个类型相同的对象。
type属性
假设有一个对象定义如下方代码所示。
/**
* 随意声明的一个业务对象定义
*/
public class MockBusinessObject {
private String dependency1;
private int dependency2;
public MockBusinessObject(String dependency) {
this.dependency1 = dependency;
}
public MockBusinessObject(int dependency) {
this.dependency2 = dependency;
}
...
@Override
public String toString() {
return new ToStringBuilder(this).append("dependency1", dependency1).append("dependency2", dependency2).toString();
}
}
该类声明了两个构造方法,分别都只是传入一个参数,且参数类型不同。这时,我们可以进行配置,如以下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg>
<value>111111</value>
</constructor-arg>
</bean>
如果从BeanFactory取得该对象并调用toString()查看的话,我们会发现Spring调用的是第一个构造方法,因为输出是如下内容:
..MockBusinessObject@f73c1[dependency1=111111,dependency2=0]
但是,如果我们想调用的却是第二个传入int类型参数的构造方法,又该如何呢?可以使用type属性,通过指定构造方法的参数类型来解决这一问题,配置内容如下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg type="int">
<value>111111</value>
</constructor-arg>
</bean>
现在,我们得到了自己想要的对象实例,如下的控制台输出信息印证了这一点:
..MockBusinessObject@f73c1[dependency1=<null>,dependency2=111111]
index属性
当某个业务对象的构造方法同时传入了多个类型相同的参数时,Spring又该如何将这些配置中的信息与实际对象的参数一一对应呢?好在,如果配置项信息和对象参数可以按照顺序初步对应的话,Spring还是可以正常工作的,如下方代码所示。
public class MockBusinessObject {
private String dependency1;
private String dependency2;
public MockBusinessObject(String dependency1, String {
this.dependency1 = dependency1;
this.dependency2 = dependency2;
}
...
@Override
public String toString() {
return new ToStringBuilder(this).append("dependency1", dependency1).append("dependency2", dependency2).toString();
}
}
并且,配置内容如以下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg value="11111"/>
<constructor-arg value="22222"/>
</bean>
那么,我们可以得到如下对象:
..MockBusinessObject@1ef8cf3[dependency1=11111,dependency2=22222]
但是,如果要让“11111”作为对象的第二个参数,而将“22222”作为第一个参数来构造对象,又该如何呢?好!可以颠倒配置项,如以下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg value="22222"/>
<constructor-arg value="11111"/>
</bean>
不过,还有一种方式,那就是像如下代码所示的那样,使用index属性:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg index="1" value="11111"/>
<constructor-arg index="0" value="22222"/>
</bean>
2. setter方法注入的XML之道
与构造方法注入可以使用<constructor-arg>
注入配置相对应,Spring为setter方法注入提供了<property>
元素。
<property>
有一个name属性(attribute),用来指定该<property>
将会注入的对象所对应的实例变量名称。之后通过value或者ref属性或者内嵌的其他元素来指定具体的依赖对象引用或者值,如以下代码所示:
<bean id="djNewsProvider" class="..FXNewsProvider">
<property name="newsListener">
<ref bean="djNewsListener"/>
</property>
<property name="newPersistener">
<ref bean="djNewsPersister"/>
</property>
</bean>
当然,如果只是使用<property>
进行依赖注入的话,请确保你的对象提供了默认的构造方法,也就是一个参数都没有的那个。
以上配置形式还可以简化为如下形式:
<bean id="djNewsProvider" class="..FXNewsProvider">
<property name="newsListener" ref="djNewsListener"/>
<property name="newPersistener" ref="djNewsPersister"/>
</bean>
使用<property>
的setter方法注入和使用<constructor-arg>
的构造方法注入并不是水火不容的。实际上,如果需要,可以同时使用这两个元素:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg value="11111"/>
<property name="dependency2" value="22222"/>
</bean>
当然,现在需要MockBusinessObject提供一个只有一个String类型参数的构造方法,并且为dependency2提供了相应的setter方法。下方代码演示了符合条件的一个业务对象定义。
public class MockBusinessObject {
private String dependency1;
private String dependency2;
public MockBusinessObject(String dependency) {
this.dependency1 = dependency;
}
public void setDependency2(String dependency2) {
this.dependency2 = dependency2;
}
...
}
3. <property>
和<constructor-arg>
中可用的配置项
之前我们看到,可以通过在<property>
和<constructor-arg>
这两个元素内部嵌套<value>
或者<ref>
,来指定将为当前对象注入的简单数据类型或者某个对象的引用。不过,为了能够指定多种注入类型,Spring还提供了其他的元素供我们使用,这包括bean、ref、idref、value、null、list、set、map、props。下面我们来逐个详细讲述它们。
(1)<value>
。可以通过value为主体对象注入简单的数据类型,不但可以指定String类型的数据,而且可以指定其他Java语言中的原始类型以及它们的包装器(wrapper)类型,比如int、Integer等。 容器在注入的时候,会做适当的转换工作(我们会在后面揭示转换的奥秘)。你之前已经见过如何使用<value>
了,不过让我们通过如下代码来重新认识一下它:
<constructor-arg>
<value>111111</value>
</constructor-arg>
<property name="attributeName">
<value>222222</value>
</property>
需要说明的是,<value>
是最“底层”的元素,它内部不能再嵌套使用其他元素了。
(2)<ref>
。使用ref来引用容器中其他的对象实例,可以通过ref的local、parent和bean属性来指定引用的对象的beanName是什么。下方代码演示了ref及其三个对应属性的使用情况。
<constructor-arg>
<ref local="djNewsPersister"/>
</constructor-arg>
或者
<constructor-arg>
<ref parent="djNewsPersister"/>
</constructor-arg>
或者
<constructor-arg>
<ref bean="djNewsPersister"/>
</constructor-arg>
local、parent和bean的区别在于:
-
local只能指定与当前配置的对象在同一个配置文件的对象定义的名称(可以获得XML解析器的id约束验证支持);
-
parent则只能指定位于当前容器的父容器中定义的对象引用;
-
bean则基本上通吃,所以,通常情况下,直接使用bean来指定对象引用就可以了。
<ref>
的定义为<!ELEMENT ref EMPTY>
,也就是说,它下面没有其他子元素可用了。
(3)<idref>
。如果要为当前对象注入所依赖的对象的名称,而不是引用,那么通常情况下,可以使用来达到这个目的,使用如下形式:
<property name="newsListenerBeanName">
<value>djNewsListener</value>
</property>
但这种场合下,使用idref才是最为合适的。因为使用idref,容器在解析配置的时候就可以帮4你检查这个beanName到底是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在。以下代码演示了idref的使用:
<property name="newsListenerBeanName">
<idref bean="djNewsListener"/>
</property>
(4)内部<bean>
。使用<ref>
可以引用容器中独立定义的对象定义。但有时,可能我们所依赖的对象只有当前一个对象引用,或者某个对象定义我们不想其他对象通过<ref>
引用到它。这时,我们可以使用内嵌的<bean>
,将这个私有的对象定义仅局限在当前对象。对于FX新闻系统的DowJonesNews-Listener而言,实际上只有道琼斯的FXNewsProvider会使用它。而且,我们也不想让其他对象引用到它。为此完全可以像下方代码这样,将它配置为内部<bean>
的形式。
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg index="0">
<bean class="..impl.DowJonesNewsListener">
</bean>
</constructor-arg>
<constructor-arg index="1">
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
这样,该对象实例就只有当前的djNewsProvider可以使用,其他对象无法取得该对象的引用。
(5)<list>
。<list>
对应注入对象类型为java.util.List
及其子类或者数组类型的依赖对象。通过<list>
可以有序地为当前对象注入以collection形式声明的依赖。下方代码给出了一个使用<list>
的实例演示。
// 使用<list>进行依赖注入的对象定义
public class MockDemoObject {
private List param1;
private String[] param2;
...
// 相应的setter和getter方法 ...
}
配置类似于
<property name="param1">
<list>
<value> something</value>
<ref bean="someBeanName" />
<bean class="..." />
</list>
</property>
<property name="param2">
<list>
<value>stringValue1</value>
<value>stringValue2</value>
</list>
</property>
(6)<set>
。如果说<list>
可以帮你有序地注入一系列依赖的话,那么<set>
就是无序的,而且, 对于set来说,元素的顺序本来就是无关紧要的。<set>
对应注入Java Collection中类型为java.util. Set
或者其子类的依赖对象。下方代码演示了通常情况下<set>
的使用场景。
// 使用<set>进行依赖注入的对象定义
public class MockDemoObject {
private Set valueSet;
// 必要的setter和getter方法
...
}
配置类似于
<property name="valueSet">
<set>
<value> something</value>
<ref bean="someBeanName" />
<bean class="..." />
<list>
...
</list>
</set>
</property>
例子就是例子,只是为了给你演示这个元素到底有多少能耐。从配置上来说,这样多层嵌套、多元素混杂配置是完全没有问题的。不过,各位在具体编程实践的时候可要小心了。如果你真的想这么夹杂配置的话,不好意思,估计ClassCastException会很愿意来亲近你,而这跟容器或者配置一点儿关系也没有。
(7)<map>
。与列表(list)使用数字下标来标识元素不同,映射(map)可以通过指定的键(key) 来获取相应的值。如果说在<list>
中混杂不同元素不是一个好的实践(方式)的话,你就应该求助<map>
。<map>
与<list>
和<set>
的相同点在于,都是为主体对象注入Collection类型的依赖,不同点在于它对应注入java.util.Map
或者其子类类型的依赖对象。下方代码演示了<map>
的通常使用场景。
// 使用<map>进行依赖注入的对象定义
public class MockDemoObject {
private Map mapping;
// 必要的setter和getter方法
...
}
配置类似于
<property name="mapping">
<map>
<entry key="strValueKey">
<value>something</value>
</entry>
<entry>
<key>objectKey</key>
<ref bean="someObject" />
</entry>
<entry key-ref="lstKey">
<list>
...
</list>
</entry>
...
</map>
</property>
对于<map>
来说,它可以内嵌任意多个<entry>
,每一个<entry>
都需要为其指定一个键和一个值, 就跟真正的java.util.Map
所要求的一样。
- 指定entry的键。可以使用
<entry>
的属性——key或者key-ref来指定键,也可以使用<entry>
的内嵌元素<key>
来指定键,这完全看个人喜好,但两种方式可以达到相同的效果。在<key>
内部,可以使用以上提到的任何元素来指定键,从简单的<value>
到复杂的Collection,只要映射需要,你可以任意发挥。 - 指定entry对应的值。
<entry>
内部可以使用的元素,除了<key>
是用来指定键的,其他元素可以任意使用,来指定entry对应的值。除了之前提到的那些元素,还包括马上就要谈到<props>
。如果对应的值只是简单的原始类型或者单一的对象引用,也可以直接使用<entry>
的value或者value-ref这两个属性来指定,从而省却多敲入几个字符的工作量。
所以,如果你不想敲那么些字符,可以像下方代码所展示的那样使用<map>
进行依赖注入的配置。
public class MockDemoObject {
private Map mapping;
// 必要的setter和getter方法
...
}
配置类似于
<property name="valueSet">
<map>
<entry key="strValueKey" value="something" />
<entry key-ref="" value-ref="someObject" />
<entry key-ref="lstKey">
<list>
...
</list>
</entry>
...
</map>
</property>
(8)<props>
。<props>
是简化后了的<map>
,或者说是特殊化的map,该元素对应配置类型为java.util.Properties
的对象依赖。因为Properties只能指定String类型的键(key)和值,所以,<props>
的配置简化很多,只有固定的格式,见下方代码。
public class MockDemoObject {
private Properties emailAddrs;
// 必要的setter和getter方法
...
}
配置类似于
<property name="valueSet">
<props>
<prop key="author">fujohnwang@gmail.com</prop>
<prop key="support">support@spring21.cn</prop> ...
</props>
</property>
每个<props>
可以嵌套多个<prop>
,每个<prop>
通过其key属性来指定键,在<prop>
内部直接指 定其所对应的值。<prop>
内部没有任何元素可以使用,只能指定字符串,这个是由java.util. Properties
的语意决定的。
(9)<null/>
。最后一个提到的元素是<null/>
,这是最简单的一个元素,因为它只是一个空元素, 而且通常使用到它的场景也不是很多。对于String类型来说,如果通过value以这样的方式指定注入, 即<value></value>
,那么,得到的结果是"",而不是null。所以,如果需要为这个string对应的值 注入null的话,请使用<null/>
。当然,这并非仅限于String类型,如果某个对象也有类似需求,请 不要犹豫。
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对剩余的对象属性进行自动绑定。
作为所有<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>