领先一步
VMware 提供培训和认证,助您加速进步。
了解更多更新:本文档中描述的 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> 到 @BeanSpring 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 和代码之间再次不是理想的。需要的是一种允许所有应用程序配置都在代码中指定的_方法。
<tx:annotation-driven> 到 TxAnnotationDriven要理解 FeatureSpecification 类,最好先考虑 Spring 的许多 XML 命名空间的性质。考虑以下内容
这些元素中的每一个都指定了 Spring 容器的一个功能的配置:组件扫描功能、注解驱动事务管理功能等。FeatureSpecification 类允许用户完全在代码中配置 Spring 容器的这些相同功能。
让我们通过从 tx:annotation-driven/ 过渡到 TxAnnotationDriven 的示例,看看 FeatureSpecification 的实际应用
<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);
}
}
Spring 容器会处理 @FeatureConfiguration 类,以期望返回 FeatureSpecifation 类型的 @Feature 方法,例如 TxAnnotationDriven。
@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 中工作时的内容辅助体验
在第一个里程碑版本中,以下 FeatureSpecification 实现已可用
ComponentScanSpecTxAnnotationDrivenMvcAnnotationDrivenMvcResourcesMvcViewControllersMvcDefaultServletHandler您将在 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 命名空间并注册自己的 NamespaceHandler 和 BeanDefinitionParser 实现一样,整个 Feature* 模型也同样开放。有关详细信息,我将参考 Javadoc,但足以说明的是,您可以编写自己的 FeatureSpecification 类型并在 @Feature 方法中轻松使用它们。
对于那些已经实现了自定义命名空间和 BeanDefinitionParsers 的人来说,您会感兴趣地知道,在框架内部,我们已经将现有的 BeanDefinitionParsers 重构为在内部委托给 FeatureSpecifications。这大大简化了解析器的实现,并确保了 XML 和 @Configuration 样式之间关键 bean 注册逻辑的重用。我们鼓励您在合适的地方考虑进行同样的重构。
虽然 Spring 在基于代码的配置方面不断进步,但必须一再强调,我们绝不打算消除、弃用或以其他方式鼓励用户逐步淘汰使用 XML 来配置容器。Spring XML 仍然很受欢迎,许多用户对其很满意,特别是考虑到 SpringSource Tool Suite 为编写 bean 配置文件提供的先进工具选项。@Configuration——以及现在的 @FeatureConfiguration——类为那些希望尽可能多地在 Java 中工作的人提供了另一种一流的配置方法。因此,请放心,Spring XML 不会消失——事实上,在 Spring 3.1 M1 中,我们对 XML 给予了足够的关注,添加了嵌套的 <beans></beans> 元素、用于更方便的构造函数注入的 c: 命名空间以及用于配置 Spring 新缓存功能的 cache: 命名空间。
我们希望您会发现 FeatureSpecification 类是现有 @Configuration 类模型的自然而强大的扩展。请查看随本博文一同提供的 示例应用程序,并请提供反馈!