Spring 3.1 M1: 引入 FeatureSpecification 支持

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

更新:此博客文章中描述的 FeatureSpecification 功能已在 Spring Framework 3.1 M2 中移除,取而代之的是 @Enable* 注解。有关更多信息,请参阅3.1 M2 公告

引言

的早期文章中,我提到了如何将新的 @Profile 注解与 @Configuration 类结合使用,以利用 Spring 的Bean 定义 Profile。今天,我们将探讨 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:*/,允许以非常简洁的方式实现大量的配置。例如,正如许多用户所知,在 XML 中启用 Spring 的注解驱动事务管理只需一行:

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


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

在底层,当处理 tx:annotation-driven 元素时,Spring 会为您创建许多支持性的基础设施级别的 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 stereotype 注解的类。

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


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

为了让容器提供参数,必须在其他地方(可能在 @Configuration 类中)声明一个类型为 PlatformTransactionManager 的 Spring Bean。这种“参数自动装配”的概念对于框架来说并不新鲜;例如,这类似于 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 方法中轻松使用它们。

对于那些已经实现了自定义命名空间和 BeanDefinitionParser 的用户,您会很高兴地知道,在框架内部,我们已经重构了现有的 BeanDefinitionParser,使其内部委托给 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 类模型自然而强大的扩展。请查看 accompanying this post 的示例应用程序,并务必提供反馈!

获取 Spring 新闻通讯

订阅 Spring 新闻通讯,保持联系

订阅

先行一步

VMware 提供培训和认证,助您加速进步。

了解更多

获取支持

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

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部