Spring 容器可以自动装配 Bean 之间的关系。你可以让 Spring 通过检查 ApplicationContext 的内容来自动为您的 bean 解析依赖者(其他 bean)。自动装配具有以下优点:
(1)自动装配可以大大减少指定属性或构造函数参数的需要。
(2)自动装配可以随着对象的发展而更新配置。例如,如果您需要向类中添加依赖项,则无需修改配置即可自动满足该依赖项。因此,自动装配在开发过程中特别有用,当代码库变得更加稳定时,不排除切换到显式装配的选择。
用基于 XML 的配置元数据时,您可以使用 <bean/> 元素的 autowire 属性为 bean 定义指定自动装配模式。自动装配功能有四种模式。您可以为每个 Bean 指定自动装配模式,从而选择要自动装配的 Bean。下面描述了四种自动装配模式:
不自动装配。Bean 引用必须由 ref 元素定义。对于大型部署,不建议更改默认设置,因为明确指定依赖会带来更强的控制力和清晰度。在某种程度上,它记录了系统的结构。
根据属性名称自动装配。Spring 会查找与需要自动装配的属性名称相同的 Bean。例如,如果一个 Bean 定义被设置为按名称自动装配,并且它包含一个 master 属性(即它有一个 setMaster(..) 方法),那么 Spring 就会查找一个名为 master 的 Bean 定义,并使用它来设置该属性。
如果果容器中正好只存在一个属性类型的 Bean,则允许对该属性进行自动装配。如果存在一个以上的 Bean,则会抛出一个异常,表示不能对该 Bean 使用 byType 自动装配。如果没有匹配的 Bean,则不会发生任何事情(不设置属性)。
constructor
与 byType 类似,但适用于构造函数参数。如果容器中没有一个构造函数参数类型的 Bean,就会出现错误。
注意
使用 byType 或 constructor 自动装配模式时,可以装配数组和类型集合。在这种情况下,容器中所有符合预期类型的自动装配候选对象都会提供,以满足依赖关系。如果预期键类型是字符串,则可以自动装配强类型的 Map 实例。自动装配的 Map 实例的值由符合预期类型的所有 Bean 实例组成,而 Map 实例的键则包含相应的 Bean 名称。
applicationContext-ioc-demo5.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" /> <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree"> <constructor-arg name="name" value="hello world" /> </bean> <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" /> </beans>
BeanOne.java:
package com.hxstrive.spring5.ioc.demo5; import java.util.Arrays; import java.util.Map; public class BeanOne { private BeanTwo beanTwo; private BeanThree beanThree; private BeanTwo[] beanTwos; private Map<String,BeanThree> beanThreeMap; @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("beanTwo=" + beanTwo); builder.append("\nbeanThree=" + beanThree); builder.append("\nbeanTwos=" + Arrays.toString(beanTwos)); builder.append("\nbeanThreeMap=" + beanThreeMap); return builder.toString(); } // 省略 getter 和 setter 方法 }
BeanTwo.java:
package com.hxstrive.spring5.ioc.demo5; public class BeanTwo { @Override public String toString() { return "BeanTwo{}"; } }
BeanThree.java:
package com.hxstrive.spring5.ioc.demo5; public class BeanThree { private String name; public BeanThree(String name) { this.name = name; } @Override public String toString() { return "BeanThree{" + "name='" + name + '\'' + '}'; } }
Main.java:
package com.hxstrive.spring5.ioc.demo5; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Bean 自动装配 * @author hxstrive.com */ public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-ioc-demo5.xml"); BeanOne beanOne = context.getBean(BeanOne.class); System.out.println(beanOne.toString()); } }
运行示例,输出如下:
beanTwo=BeanTwo{} beanThree=BeanThree{name='hello world'} beanTwos=[BeanTwo{}] beanThreeMap={beanThree=BeanThree{name='hello world'}}
装配在整个项目中一致使用时效果最好。如果通常不使用自动装配,那么仅使用它来连接一个或两个 bean 定义可能会使开发人员感到困惑。
考虑自动装配的局限性和缺点:
属性和构造函数参数设置中的显式依赖关系总是覆盖自动装配。您不能自动装配简单的属性,如原始类型、字符串和类 (以及这些简单属性的数组)。这种限制是设计所致。
自动装配不如显式装配精确。不过,正如前面所指出的,Spring 会小心地避免猜测,以免歧义可能产生意想不到的结果。不再显式地记录 Spring 管理的对象之间的关系。
Spring 容器生成文档的工具可能无法获得连接信息。
容器中的多个 Bean 定义可能与 setter 方法或构造器参数指定的类型相匹配,从而自动装配。对于数组、集合或 Map 实例,这不一定是个问题。但是,对于预期只有一个值的依赖关系,这种模糊性就不能任意解决了。如果没有可用的唯一 bean 定义,就会出现异常。
一种情况下,您有几种选择:
放弃自动装配,转而使用显式装配。
通过将 bean 定义的 autowire-candidate 属性设置为 false,可以避免为 bean 定义自动布线。例如:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" /> <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree"> <constructor-arg name="name" value="hello world" /> </bean> <!-- 禁止该 bean 被自动装配 --> <bean id="beanThree2" class="com.hxstrive.spring5.ioc.demo5.BeanThree" autowire-candidate="false"> <constructor-arg name="name" value="hi! spring" /> </bean> <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" /> </beans>
运行示例,输出如下:
beanTwo=BeanTwo{} beanThree=BeanThree{name='hello world'} beanTwos=[BeanTwo{}] beanThreeMap={beanThree=BeanThree{name='hello world'}}
通过将 <bean/> 元素的 primary 属性设置为 true,将单个 Bean 定义指定为主要候选。例如:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" /> <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree"> <constructor-arg name="name" value="hello world" /> </bean> <!-- 禁止该 bean 被自动装配 --> <bean id="beanThree2" class="com.hxstrive.spring5.ioc.demo5.BeanThree" autowire-candidate="false"> <constructor-arg name="name" value="hi! spring" /> </bean> <bean id="primaryBeanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree" primary="true"> <constructor-arg name="name" value="primary beanThree" /> </bean> <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" /> </beans>
运行示例,输出如下:
beanTwo=BeanTwo{} beanThree=BeanThree{name='primary beanThree'} beanTwos=[BeanTwo{}] beanThreeMap={beanThree=BeanThree{name='hello world'}, primaryBeanThree=BeanThree{name='primary beanThree'}}
通过基于注释的配置实现更精细的控制。
根据每个 Bean 的情况,您可以将某个 Bean 排除在自动装配之外。在 Spring 的 XML 格式中,将 <bean/> 元素的 autowire-candidate 属性设置为 false。Spring 容器会使该 Bean 定义对自动装配基础架构不可用(包括注解样式配置,如 @Autowired)。
注意:autowire-candidate 属性只影响基于类型(byType)的自动装配。它不会影响通过名称(byName)进行的显式引用,即使指定的 Bean 没有被标记为自动装配候选对象,这种引用也会被解析。因此,如果名称匹配,按名称自动装配仍会注入一个 Bean。
您还可以根据与 Bean 名称的模式匹配来限制自动装配候选对象。顶层 <beans/> 元素的 default-autowire-candidates 属性可接受一个或多个模式。例如,要将自动装配候选状态限制为名称以 Repository 结尾的任何 Bean,可提供 *Repository 值。要提供多个模式,请用逗号分隔的列表定义它们。Bean 定义的 autowire-candidate 属性的显式值 true 或 false 始终优先。对于此类 Bean,模式匹配规则并不适用。
对于您永远不想通过自动装配注入到其他 Bean 中的 Bean,这些技术非常有用。这并不意味着被排除的 Bean 本身不能使用自动装配进行配置。相反,Bean 本身并不是自动装配其他 Bean 的候选对象。