Spring 3.1 M1:引入 FeatureSpecification 支持

工程 | Chris Beams | 2011年2月17日 | ...

更新:此博文中描述的FeatureSpecification功能已在 Spring Framework 3.1 M2 中被@Enable*注解取代。更多信息请参见3.1 M2 公告

介绍

在之前的文章系列中,我提到了如何将新的@Profile注解与@Configuration类结合使用,以利用 Spring 的 *bean 定义配置文件*。今天,我们将介绍 Spring 3.1 中代码配置领域的一个全新补充:*FeatureSpecification类*及其相关的支持。

我为此篇文章准备了一个示例项目。请访问https://github.com/cbeams/spring-3.1-featurespec 并按照README中的说明操作。

@Bean

Spring 3.0 中添加的@Configuration类支持,实质上提供了一种用 Java 而不是 XML 来编写 bean 定义的机制。例如

/src/main/com/bank/config/xml/transfer-service-config.xml


<beans>
	<bean id="accountRepository" class="com.bank.repository.internal.JdbcAccountRepository">
		<constructor-arg ref="dataSource"/>
	</bean>

	<!-- 'dataSource' and other bean definitions -->
</beans>

可以转换为以下@Configuration

/src/main/com/bank/config/xml/TransferServiceConfig.java


@Configuration
public class TransferServiceConfig {

	@Bean
	public AccountRepository accountRepository() {
		return new JdbcAccountRepository(dataSource());
	}

	@Bean
	public DataSource dataSource() {
		// create, configure and return the DataSource ...
	}
}

而 XML 配置将如下启动


new GenericXmlApplicationContext("com/bank/config/xml/transfer-service-config.xml");

@Configuration的启动方式类似,但以类作为输入


new AnnotationConfigApplicationContext(TransferServiceConfig.class);

这种方法效果很好,但它仅限于表达单个 bean 定义——到目前为止,@Configuration世界中还没有等同于 Spring XML 命名空间的支持。Spring 的 XML 命名空间,例如context:*/tx:*/允许以非常简洁的方式进行大量的配置。例如,许多用户都知道,启用 Spring 的注解驱动事务管理在 XML 中只是一行代码

/src/main/com/bank/config/xml/transfer-service-config.xml


<tx:annotation-driven transaction-manager="txManager"/>

在幕后,当处理tx:annotation-driven元素时,会代表您创建许多支持性基础架构级别的 bean 定义,您无需直接关心这些定义,这是一件好事——它 *可以正常工作*,确保任何用@Transactional标记的 Spring bean 都用TransactionInterceptor进行代理。在 Spring 3.1 M1 之前,为了在@Configuration类中模拟此功能,您必须将这些基础架构 bean 作为单个@Bean方法自己声明。有些人实际上已经这么做了,我认为可以肯定地说这不是一个首选方法。为了避免这种额外的努力,Spring 3.0 提供了@ImportResource注解,允许@Configuration类在需要命名空间时选择性地“引入”XML 配置

/src/main/com/bank/config/code/TransferServiceConfig.java


@Configuration
@ImportResource("com/bank/config/xml/tx-config.xml")
public class TransferServiceConfig {
	// ...
}

在上面的示例中,tx-config.xml将包含tx:annotation-driven/声明。这种混合模型很好用,但同样,将配置拆分为 XML 和代码并不是理想的做法。我们需要一种方法允许 *所有* 应用程序配置都用代码指定。

TxAnnotationDriven

要理解FeatureSpecification类,最好首先考虑许多 Spring XML 命名空间的性质。考虑以下内容:

这些元素中的每一个都 *指定* Spring 容器的一个 *功能* 的配置:组件扫描功能、注解驱动事务管理功能等。*FeatureSpecification* 类允许用户完全用代码配置 Spring 容器的这些相同功能。

让我们看看FeatureSpecification的实际应用,以从tx:annotation-driven/TxAnnotationDriven的转换为例


<beans>
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

变为

/src/main/com/bank/config/code/TxFeature.java


@FeatureConfiguration
class TxFeature {

	@Feature
	public TxAnnotationDriven txAnnotationDriven(PlatformTransactionManager txManager) {
		return new TxAnnotationDriven(txManager);
	}

}

@FeatureConfiguration类由 Spring 容器处理,预期的是返回FeatureSpecifation类型(如TxAnnotationDriven)的@Feature方法。

@FeatureConfiguration类被设计为@Configuration类的配套类。启动容器时,两者可以互换使用


ApplicationContext ctx =
	new AnnotationConfigApplicationContext(TransferServiceConfig.class, TxFeatures.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...

并且一个可以@Import另一个


@Configuration
@Import(TxFeature.class)
public class TransferServiceConfig { ... }

或者


@FeatureConfiguration
@Import(TransferServiceConfig.class)
public class TxFeature { ... }

@FeatureConfiguration类可以包含一个或多个@Feature方法。例如,我们可以在同一个类中配置组件扫描和注解驱动事务管理


@FeatureConfiguration
class MyFeatures {

	@Feature
	public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
		return new TxAnnotationDriven(txManager);
	}

	@Feature
	public ComponentScanSpec scan() {
		return new ComponentScanSpec("com.bank.service");
	}

}

@Feature方法在启动时由容器调用,返回的FeatureSpecification对象被处理以配置相关功能的内部细节。对于TxAnnotationDriven,配置代理创建,注册事务拦截器 bean 等;对于ComponentScanSpec,使用指定的参数配置ClassPathBeanDefinitionScanner,然后运行以检测和注册用@Component或其他 Spring 原型注解标记的类。

@Feature方法可以接受参数以获取对 Spring bean 的引用


	@Feature
	public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
		return new TxAnnotationDriven(txManager);
	}

为了使参数由容器提供,必须在其他地方声明一个类型为PlatformTransactionManager的 Spring bean,可能在@Configuration类中。这种“参数自动装配”的概念对于框架来说并不新鲜;例如,这类似于 Spring MVC 中@InitBinder@RequestMapping方法的工作方式。能够引用实际的 Spring bean 实例通过避免使用基于字符串的 bean 名称来保持类型安全。

框架随附的FeatureSpecification实现旨在在编写@Feature方法时最大限度地易于使用。如下面的屏幕截图所示,ComponentScanSpec方法是 *可链接的*,并且该类的公共 API 已精心设计,可在 IDE 中工作时优化内容辅助体验

Content-assist for ComponentScanSpec

工作状态

以下 FeatureSpecification 实现已随第一个里程碑版本提供

  • ComponentScanSpec
  • TxAnnotationDriven
  • MvcAnnotationDriven
  • MvcResources
  • MvcViewControllers
  • MvcDefaultServletHandler

您将在 Rossen 此系列中即将发布的文章中看到许多Mvc*类型,该文章介绍了 Spring 3.1 M1 中对 Spring MVC 的增强。

在第二个里程碑版本中,我们将为更多常用的命名空间元素添加规范实现,例如context:mbean-export/aop:aspectj-autoproxy/等等。

请注意,并非所有 Spring XML 命名空间元素都相同。有些元素(如前面提到的那些元素)用于配置容器功能,而其他元素(如jee:jndi-lookup/util:*/命名空间中的所有内容)只是方便的实用程序,用于在 XML 中执行在代码中无需任何特殊支持即可轻松执行的操作。因此,不要期望 Spring XML 元素和FeatureSpecification类型之间存在一对一的映射。只有那些有意义的元素才会被转换为代码。

开放模型

正如用户可以定义自己的 Spring XML 命名空间并注册自己的NamespaceHandlerBeanDefinitionParser实现一样,整个Feature*模型也同样开放。我将推迟到Javadoc以获取完整详细信息,但可以肯定地说,您可以编写自己的FeatureSpecification类型,并轻松地在@Feature方法中使用它们。

对于那些已经实现自定义命名空间和BeanDefinitionParsers的用户,您会很感兴趣地了解到,在框架内,我们已经重构了现有的BeanDefinitionParsers以在内部委托给FeatureSpecifications。这大大简化了解析器的实现,并确保在 XML 和@Configuration样式之间重用关键的 bean 注册逻辑。我们鼓励您在适当的时候考虑进行相同的重构。

给快乐的 XML 用户的一则说明

虽然 Spring 继续推进基于代码的配置,但必须反复指出的是,我们绝不会消除、弃用或鼓励用户逐步淘汰使用 XML 来配置容器。Spring XML 仍然很流行,许多用户对此感到满意,特别是考虑到 SpringSource Tool Suite 为编写 bean 配置文件提供的先进工具选项。@Configuration——现在是@FeatureConfiguration——类为那些尽可能多地使用 Java 的用户提供了另一种一等配置方法。所以请放心,Spring XML 不会消失——实际上,在 Spring 3.1 M1 中,我们通过添加嵌套的元素、更方便的构造函数注入的c:命名空间以及配置 Spring 新缓存功能的cache:命名空间,对 XML 给予了充分的关注。

总结

我们希望您会发现FeatureSpecification类是现有@Configuration类模型的自然且强大的扩展。请查看本文附带的示例应用程序,并请提供反馈!

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进度。

了解更多

获得支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部