在Spring中,对象无需自己查找或创建与其所关联的其他对象。相反,容器负责把需要相互协作的对象引用赋予各个对象。
创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入的本质。
2.1 Spring配置的可选方案
Spring提供了三种主要的装配机制:
- 在XML中进行显式配置;
- 在Java中进行显式配置;
- 隐式的bean发现机制和自动装配。
作者建议尽可能的使用自动装配的机制。显式配置越少越好,当必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现是才应该使用XML。
2.2 自动化装配bean
Spring从两个角度实现自动化装配:
- 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean;
- 自动装配(autowiring):Spring自动满足bean之间的依赖。
2.2.1 创建可被发现的bean
在类上使用@Component注解,这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
组件扫描默认是不启用的,还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类并为其创建bean。
@Configuation
@ComponentScan
public class CDPlayerConfig {
}
如果没有其他配置,@ComponentScan默认会扫描与配置类相同的包。
如果想使用XML来启用组件扫描,可以使用Spring context命名空间的context:component-scan元素。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:Context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframeword.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="soundsystem"/>
</beans>
测试组件扫描代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerText {
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd)
}
}
CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。
2.2.2 为组件扫描的bean命名
在组件扫描中如果没有明确的设置ID,Spring会根据类名为其指定一个ID。这个bean所给定的ID是首字母变为小写的类名。
如果想为这个bean设置不同的ID,需要将其作为值传递给@Component注解。
@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
}
另外一种bean命名方式使用Java依赖注入规范中提供的@Named注解:
@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
}
Spring支持将@Named作为@Component注解的替代方案。两者之间由一些细微的差异,但在大多数场景中可以互换。
本书的作者更喜欢@Component。
2.2.3 设置组件扫描的基础包
如果没有为@ComponentScan设置属性,按照默认规则,它会以配置类所在的包作为基础包来扫描逐渐。
如果想扫描不同的包,在@ComponentScan的value属性中指定包名即可。
@Configuraion
@ComponentScan("soundsystem")
public class CDPlayerConfig{
}
如果想更加清晰的表明所设置的是基础包,可以通过basePackages属性进行配置:
@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{}
basePackages属性可以设置多个包,赋值一个数组即可:
@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig { }
除了将包设置为简单的String类型外,@ComponentScan哈提供了另外一种方法,将其指定为包中所包含的类或接口:
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig { }
2.2.4 通过为bean添加注解实现自动装配
可以在构造器、setter方法、任何方法上使用@Autowired注解,Spring会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求,那么这个bean将会被装配进来。
如果没有匹配的bean,那么在应用上下文创建的时候会抛出异常。可以将@Autowired的required属性设置为false,此时Spring会尝试自动装配,但是如果没有匹配的bean的话该属性将会为null。
如果有多个bean都满足依赖关系,Spring将会抛出异常。
@Autowired是Spring特有的注解。可以使用@Inject注解替代。
2.3 通过Java代码装配Bean
显式的装配方式有两种:Java和XML。
2.3.1 创建配置类
@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。
2.3.2 声明简单bean
要在JavaConfig中声明bean,需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。
@Bean注解会告诉Spring这个方法将返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。
默认情况下,bean的ID与方法名一致。如果想为其设置一个不同的名字,可以重命名该方法,也可以通过@Bean的name属性指定。
2.3.3 借助JavaConfig实现注入
在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。在方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
通过调用方法引用bean的方式有点令人困惑。还有一种理解起来更简单的方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
cdPlaye()方法请求一个CompactDisc作为参数,当Spring调用cdPlayer()创建CDPlayer bean的时候,它会自动装配一个CompactDisc到配置方法之中。
通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。可以将配置分散到多个配置类、XML文件以及自动扫面和装配bean之中。
2.4 通过XML装配Bean
2.4.1 创建XML配置规范
在XML配置中,要创建一个XML文件,并且要以<beans>元素为根。
<?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.springframeword.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<!-- 配置详情 -->
</beans>
2.4.2 声明一个简单的<bean>
<bean>元素类似于JavaConfig中的@Bean注解。
如果没有给定ID,所以这个bean将会根据全限定类名来进行命名。例如“soundsystem.SgtPeppers#0“,其中“#0”是一个计数形式,用来区分相同类型的其他bean。
2.4.3 借助构造器注入初始化bean
构造器注入有两种基本的配置方案可供选择:
- <constructon-arg>元素;
- 使用Spring 3.0 所引入的c-命名空间。
构造器注入bean引用
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc"/>
</bean>
c-命名空间的schema:
xmlns:c="http://www.springframework.org/schema/c"
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:cd-ref="compactDisc" />
属性以c:开头,也就是命名空间的前缀。接下来是要装配的构造器参数名,在此之后是-ref,这是一个命名约定,告诉Spring正在装配的是一个bean应用。
可以使用参数在整个参数列表中的位置来替代参数名:
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_0-ref="compactDisc" />
因为在XML中不允许数字作为属性的第一个字符,因此必须要添加一个下划线作为前缀。
如果只有一个参数,不用标示参数:
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_-ref="compactDisc" />
将字面量注入到构造器中
<bean id="compactDisc"
class="soundsystem.BlankDisc">
<constructor-arg value="sgt. Pepper" />
</bean>
依然使用<constructor-arg>元素进行注入,但是使用了value属性。
<bean id="compactDisc"
class="soundsystem.BlankDisc"
c:_title="sgt. Pepper"/>
使用c-命名空间装配字面量与装配引用的区别在于属性名中去掉了-ref后缀。
装配集合
<constructor-arg>可以将集合装配到构造器参数重,而c-命名空间不能。
可以使用<null/>元素将null传递给构造器。
可以使用<list>元素声明一个列表:
<constructor-arg>
<list>
<value>.....</value>
<value>......</value>
</list>
</constructor-arg>
<list>元素是<constructor-arg>的子元素,这表明一个包含值的列表将会传递到构造器中。其中<value>元素用来指定列表中的每个元素。
与之类似,可以使用<ref>元素代替<value>,实现bean引用列表的装配。
可以用相同的方式使用<set>元素。<set>和<list>元素的区别不大,无论在哪种情况下,<set>或<list>都可以用来装配List、Set甚至是数组。
2.4.4 设置属性
<property>元素为属性的setter方法所提供的功能与<constructor-arg>元素为构造器提供的功能是一样的。
Spring提供了p-命名空间作为<property>元素的替代方案。p-命名空间的schema:
xmlns:p="http://www.springframework.org/schema/p"
util-命名空间所提供的功能之一是util:list元素,它会创建一个列表的bean。
xmlns:util="http://www.springframework.org/schema/util"
<util:list id="trackList">
<value>......</value>
<value>.....</value>
</util:list>
下表展示了Spring util-命名空间中的元素
元素 | 描述 |
---|---|
util:constant | 引用某个类型的public static域,并将其暴露为bean |
util:list | 创建一个java.util.List类型的bean,其中包含值或引用 |
util:map | 创建一个java.util.Map类型的bean,其中包含值或引用 |
util:properties | 创建一个java.util.Propertie类型的bean |
util:property-path | 引用一个bean的属性(或内嵌属性),并将其暴露为bean |
util:set | 创建一个java.util.Set类型的bean,其中包含值或引用 |
2.5 导入和混合配置
2.5.1 在JavaConfig中引用XML配置
使用@Import注解引用其他JavaConfig配置,使用@ImportResource注解引入XML配置。
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig {
}
2.5.2 在XML配置中引用JavaConfig
在XML中可以使用<import>元素来引用其他XML配置。
将JavaConfig作为bean使用<bean>元素导入XML配置文件。