1 IoC 容器
1.4.3 使用depends-on
如果一个bean是另一个bean的依赖项,这通常意味着将一个bean设置为另一个bean的属性。通常使用基于xml的配置元数据中的<ref/>元素来完成此任务。然而,有时候bean之间的依赖关系不那么直接。例如,当需要触发类中的静态初始化器时,例如注册数据库驱动程序时。依赖属性可以显式地强制在使用此元素的bean初始化之前对一个或多个bean进行初始化。下面的示例使用depends-on属性来表示对单个bean的依赖:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
为了表示对多个bean的依赖,提供一个bean名称列表作为依赖属性的值(逗号、空格和分号是有效的分隔符):
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on属性既可以指定一个初始化时间依赖项,在单例模式Bean的情况时,也可以指定一个对应的销毁时依赖项。与给定bean定义依赖关系的依赖bean首先被销毁,先于给定bean本身被销毁。因此,依赖也可以控制销毁顺序。
1.4.3 延迟加载Beans
默认情况下,ApplicationContex会尽快实现创建和配置所有单例Beans 来作为初始化过程的一部分。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是几小时甚至几天之后。当此行为不可取时,您可以通过将bean定义标记为延迟初始化来防止单例bean的预实例化。延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。
在XML中,这种行为是由元素上的lazy-init属性控制的,如下面的示例所示:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
当前面的配置被ApplicationContext使用时,当ApplicationContext启动时,lazy bean不会被预先实例化,而not.lazy bean会立即预先实例化。
然而,当延迟初始化的bean是未延迟初始化的单例bean的依赖项时,ApplicationContext在启动时创建延迟初始化的bean,因为它必须满足单例的依赖项。延迟初始化的bean被注入到一个没有延迟初始化的单例bean中。
您还可以使用元素上的default-lazy-init属性来控制容器级别的延迟初始化,如下面的示例所示:
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
1.4.5 自动装配协作者
Spring容器可以自动创建协作bean之间的关系。通过检查ApplicationContext的内容,您可以让Spring为您的bean自动解析协作者(其他bean)。自动装配有以下优点:
- 自动装配可以大大减少指定属性或构造函数参数的需要。在这方面,本章其他地方讨论的其他机制(如bean模板)也很有价值。
- 自动装配可以随着对象的演化更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置。因此,自动装配在开发过程中特别有用,当代码库变得更加稳定时,自动装配可以无需否定切换到显式连接的选项。
在使用基于xml的配置元数据(请参阅依赖项注入)时,可以使用<bean/>元素的autowire属性为bean定义指定autowire模式。自动装配功能有四种模式。您可以为每个bean指定自动装配,因此可以选择自动装配哪些bean。下表描述了四种自动装配模式:
Mode | Explanation |
---|---|
no | (默认)没有自动装配。Bean引用必须由ref元素定义。对于较大的部署,不建议更改默认设置,因为显式地指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了一个系统的结构。 |
byName | 通过属性名自动装配。Spring寻找与需要自动装配的属性同名的bean。例如,如果一个bean定义被按名称设置为自动装配,并且它包含一个主属性(也就是说,它有一个setMaster(…)方法),Spring会查找一个名为master的bean定义并使用它来设置属性。 |
byType | 如果容器中恰好存在该属性类型的一个bean,则允许自动获取该属性。如果存在多个,则抛出一个致命异常,这表明您不能为该bean使用byType自动装配。如果没有匹配的bean,则什么也不会发生(属性没有设置)。 |
constructor | 类似于byType,但适用于构造函数参数。如果容器中没有一个构造函数参数类型的bean,则会引发致命错误。 |
使用byType或构造函数自动装配模式,您可以连接数组和类型化集合。在这种情况下,将提供容器中与预期类型匹配的所有autowire候选对象,以满足依赖性。如果期望的键类型是String,则可以自动装配强类型映射实例。自动装配映射实例的值由所有匹配期望类型的bean实例组成,映射实例的键包含相应的bean名称。
自动装配的局限性和缺点
自动装配在跨项目一致使用时效果最好。如果通常不使用自动装配,那么开发人员使用它来连接一个或两个bean定义可能会感到困惑。
考虑自动装配的局限性和缺点:
- 属性和构造参数设置中的显式依赖项总是覆盖自动装配。您不能自动装配简单的属性,如基本类型、Strings和Classes(以及这些简单属性的数组)。这种限制是由设计造成的。
- 自动装配不如显式连接精确。尽管如前面的表中所述,Spring小心地避免猜测,以免产生可能产生意外结果的歧义。spring管理对象之间的关系不再明确记录。
- 装配信息可能对从Spring容器生成文档的工具不可用。
- 容器中的多个bean定义可能与要自动装配的setter方法或构造函数参数指定的类型匹配。对于数组、集合或map实例,这不一定是个问题。然而,对于期望单个值的依赖项,这种模糊性不是任意解决的。如果没有唯一的bean定义可用,则抛出异常。
在后面的应用场景中, 你有以下几个选择:
- 放弃自动装配,支持显式装配。
- 通过将bean定义的autowire-candidate属性设置为false来避免自动装配,如下一节所述
- 通过将其<bean/>元素的primary设置为true,将单个bean定义指定为主候选。
- 实现基于注解的配置提供的更细粒度的控制,如Annotation-based Container Configuration.所述。
从自动装配中排除一个Bean
在每个bean的基础上,可以将bean排除在自动装配之外。在Spring的XML格式中,将元素的autowire-candidate属性设置为false。容器使特定的bean定义对自动装配基础设施(包括诸如@Autowired之类的注释样式配置)不可用。
autowire-candidate属性被设计为只影响基于类型的自动装配。它不会影响按名称显式引用,即使指定的bean没有标记为autowire候选bean,也会解析这些引用。因此,如果名字匹配,自动装配仍然会注入一个bean。
您还可以根据bean名称的模式匹配来限制自动装配候选对象。顶级、元素在其default-autowire-candidate属性中接受一个或多个模式。例如,要将autowire候选状态限制为名称以Repository结尾的任何bean,请提供一个值*Repository。要提供多个模式,请在逗号分隔的列表中定义它们。bean定义的autowire-candidate属性的显式值true或false总是优先。对于这样的bean,不适用模式匹配规则。
这些技术对于那些您不希望通过自动装配将其注入到其他bean中的bean非常有用。这并不意味着不能通过使用自动装配来配置被排除的bean。相反,bean本身不是自动装配其他bean的候选对象。