Spring 配置的可选方案

Spring 创建应用对象之间协作关系的行为通常称为 装配(wiring),这也是 依赖注入(DI)的本质。
而 Spring 创建 bean 具有很大的灵活性,它提供了三种主要的装配机制:

  • 隐式的 bean 发现机制和自动装配
  • 在 Java 中进行显示配置(JavaConifg)
  • 在 XML 中进行显示配置

1 通过 Java 代码装配 bean

在显式配置时,相比于 XML 配置,JavaConfig 是更好的方案,因为他更为强大、类型安全并且对重构友好,JavaConfig 是配置代码,这意味着它不应该包含任何业务逻辑。

1.1 创建配置类

1.1.1 @Configuration

创建 JavaConfig 类的关键在于为其添加 @Configuration 注解,该注解声明这个类是一个配置类,该类应该包含在 Spring 应用上下文中如何创建 bean 的细节,比如使用组件扫描的方式或直接显式配置。

1.1.2 @Bean

要在 JavaConfig 中声明 bean,需要编写一个方法,并使用 @Bean 注解这个方法,@Bean 注解会告诉Spring 这个方法将会返回一个对象,该对象要注册为 Spring 应用上下文中的 bean

既然是声明一个方法并返回一个实例,那么方法体中可以包含了最终产生 bean 实例的逻辑,这是使用 XML 配置 Spring 无法实现的功能,如:

@Bean
public CD cd() {
	int choice = (int) Math.floor(Math.random() * 4);
	if (choice == 0) {
		return new CDOfTaka();
	}
	return new CDOfOOR();
}

在 JavaConfig 中实现注入(DI)同样使用 @Bean 注解,如:

@Bean
public CD cd() {
	return new CD();
}

@Bean
public CDPlaer cdPlayer() {
	return new CDPlayer(cd());
}

@Bean
public CDPlayer cdPlayerBak(CD cd) {
	return new CDPlayer(cd);
}

看起来,cdPlayer 的返回结果是通过调用 cd() 得到的,当实际并非如此,因为 cd() 方法上添加了 @Bean 注解,Spring 将会连接所有对它的调用,并确保直接返回该方法所创建的 bean,而不是每次都对其进行实际的调用
cdPlayerBak 看起来就更好理解了,它请求一个 CD 作为参数,当 Spring 调用 cdPlayerBak 方法创建 bean 的时候,它会自动装配一个 CD bean到配置方法中。通过这个方式来引用其他的 bean 就无需要求被注入的 bean 声明到同一个配置类之中,甚至无需使用 JavaConfig 中声明。

2 通过 XML 装配 bean

使用 XML 文件装配就比较复杂了,在 JavaConfig 中只需要 @Configuration 注释,但使用 XML 时,就需要在配置文件的顶部声明多个 XML 模式文件,这些文件定义了配置 Spring 的 XML 元素。
<beans> 元素是该模式中的一个元素,它是所有 Spring 配置文件的根元素。

2.1 <bean>

在 XML 的 Spring 配置中声明一个 bean,需要在 <beans> 根元素下声明 <bean> 元素。<bean> 元素类似 JavaConfig 中的 @Bean 注解,创建这个 bean 的类通过 class 属性指定,该属性需要使用全限定的类名。

2.1.1 使用构造器注入初始化 bean

在 XML 中使用到构造器注入,可以使用 <constructor-arg> 元素,如:

<bean class="top.seiei.CD" id="cd"></bean>
<bean id="cdPlayer" class="top.seiei.CDPlayer">
	<constructor-arg ref="cd">
</bean>

2.1.2 将字面量注入到构造器中

以上的案例都是类型的转配,如果想要使用字面量值来配置对象,只需要给 <bean> 元素的 value 属性赋值,eg:

<bean id="cdPlayer" class="top.seiei.CDPlayer">
	<constructor-arg value="The Beatles" />
</bean>

2.1.3 装配集合

装配使用到集合就需要使用 <list> 元素:

<bean id="cdPlayer" class="top.seiei.CDPlayer">
	<constructor-arg>
		<list>
			<value>Taka</value>
			<value>Seiei</value>
		</list>
	</constructor-arg>
</bean>

<bean id="cdPlayerBak" class="top.seiei.CDPlayer">
	<constructor-arg>
		<list>
			<ref bean="top.seiei.Taka"/>
			<ref bean="top.seiei.Seiei"/>
		</list>
	</constructor-arg>
</bean>

2.1.4 注入属性

XML 中的属性注入需要使用 <property> 标签,eg:

<bean id="cdPlayer" class="top.seiei.CDPlayer">
	<!-- 同理使用可以使用 value 属性 -->
	<property name="title" ref="titleObj">
</bean>

3 自动化装配 bean

Spring 是使用以下的方式来实现自动化装配:

  • 组件扫描:Spring 会自动发现 应用上下文 中所创建的 bean
  • 自动装配:Spring 自动满足 bean 之间的依赖

3.1 创建可被发现的 bean

3.1.1 @Component 注释

在自动化装配 bean 中,在类的声明前使用 @Component 注释可以表明该类会作为组件类,并在 Spring 扫描的时候告知 Spring 要为这个类自动创建 bean,而没有必要显式配置 bean。
但是,组件扫描默认是不启动的,还需要显式配置一下 Spring,从而命令它去寻找带有 @Component 注解的类,并为它创建 bean。

3.1.2 自动扫描

Spring 的自动扫描实现可以在 JavaConfig 的配置文件中声明,也可以在 XML 配置文件中进行声明。
JavaConfig 使用 @ComponentScan 注解启动组件扫描,如:

@Configuration
@ComponentScan(value = "top.seiei.bean")
public class CDPlayerConfig {
}

如果没有 @ComponentScan 设置任何属性值,这意味着将按照默认规则,它会以配置类所在的包作为 基础包(base package)来扫描组件。
如果想要扫描多个基础包,可以使用 basePackages 属性,该属性接受一个数组,如:

@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig {}

3.1.3 bean 实现自动装配

自动装配是让 Spring 自动满足 bean 依赖的一种方法,即在 Spring 应用上下文中寻找匹配某个 bean 需求的其他 bean。为了声明要进行自动配装,使用 @Autowired 注解。
@Autowired 注解有一个 required 属性,它默认值为 true,即如果在 Spring 上下文中没有找到相应的 bean 进行装配,那么就会报错,此时为了避免该情况可以将其设置为 false

4 混合配置

在 Spring 应用中,可能会同时使用自动化和显式配置,也可能会同时使用 JavaConfig 和 XML 实现显式配置。

4.1 JavaConfig 中引入其它配置

在 JavaConfig 中使用 @Import 注解可以导入其它的 JavaConfig 类,而需要导入 XML 配置时,可以使用 @ImportResource 注解例如:

@Configuration
@Import({CDConfig.class, CDPlayerConfig.class})
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig{}

4.2 XML 配置中引入其它配置

在 XML 中可以使用 <import> 标签来导入其它的 XML 配置文件,直接使用 <bean> 标签可以直接引入 JavaConfig 配置类,eg:

<import resource="cd-config.xml">
<bean class="soundsystem.CDConfig"></bean>
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐