领先一步
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
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 中工作时优化内容辅助体验
以下 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 命名空间并注册自己的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 中,我们通过添加嵌套的
元素、更方便的构造函数注入的c:
命名空间以及配置 Spring 新缓存功能的cache:
命名空间,对 XML 给予了充分的关注。
我们希望您会发现FeatureSpecification
类是现有@Configuration
类模型的自然且强大的扩展。请查看本文附带的示例应用程序,并请提供反馈!