Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreUPDATE: The FeatureSpecification
functionality described in this blog post was removed in Spring Framework 3.1 M2 in favor of @Enable*
annotations. See the 3.1 M2 announcement for more information.
Earlier in this series I touched on how the new @Profile
annotation can be used in conjunction with @Configuration
classes to take advantage of Spring's bean definition profiles. Today, we'll look at an entirely new addition to the code-based configuration landscape in Spring 3.1: FeatureSpecification
classes and their related support.
I've put together a sample project to accompany this post. Find it at https://github.com/cbeams/spring-3.1-featurespec and follow the instructions in the README.
to @Bean
The support for @Configuration
classes added in Spring 3.0 essentially provides a mechanism for writing bean definitions in Java instead of XML. For example:
/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>
can be translated into the following @Configuration
class:
/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 ...
}
}
And where the XML configuration would be bootstrapped as follows:
new GenericXmlApplicationContext("com/bank/config/xml/transfer-service-config.xml");
the @Configuration
would be bootstrapped similarly, but taking the class as input:
new AnnotationConfigApplicationContext(TransferServiceConfig.class);
This approach works well, but it is limited to expressing individual bean definitions -- to date there has been no support equivalent to Spring's XML namespaces in the @Configuration
world. Spring's XML namespaces, such as context:*/
and tx:*/
allow for a tremendous amount of configuration to happen in a very concise fashion. For example, and as many users will know, enabling Spring's annotation-driven transaction management is a one-liner in XML:
/src/main/com/bank/config/xml/transfer-service-config.xml
<tx:annotation-driven transaction-manager="txManager"/>
Under the covers, when the tx:annotation-driven
element is processed, a number of supporting infrastructure-level bean definitions are created on your behalf, none of which you need directly care about, and that's a good thing -- it just works, ensuring that any of your Spring beans marked with @Transactional
are proxied with a TransactionInterceptor
. Prior to Spring 3.1 M1, in order to emulate this functionality in a @Configuration
class, you would have had to declare each of these infrastructure beans yourself as individual @Bean
methods. This is a path some have actually gone down, and I think it's safe to say it is not a preferred approach. To help avoid this extra effort, Spring 3.0 provided the @ImportResource
annotation to allow @Configuration
classes to "pull in" XML configuration selectively when namespaces are needed:
/src/main/com/bank/config/code/TransferServiceConfig.java
@Configuration
@ImportResource("com/bank/config/xml/tx-config.xml")
public class TransferServiceConfig {
// ...
}
In the example above, tx-config.xml
would contain the tx:annotation-driven/
declaration. It's nice that this hybrid model works, but again, it's not exactly ideal having to split configuration between XML and code. What's needed is an approach that allows all application configuration to be specified in code.
to TxAnnotationDriven
To understand FeatureSpecification
classes, it's best to think first about the nature of many of Spring's XML namespaces. Consider the following:
Each of these elements specifies the configuration of a feature of the Spring container: the component-scanning feature, the annotation-driven transaction management feature, etc. FeatureSpecification
classes allow users to configure these same features of the Spring container entirely in code.
Let's see a FeatureSpecification
in action, with the transition from tx:annotation-driven/
to TxAnnotationDriven
as an example:
<beans>
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
becomes:
/src/main/com/bank/config/code/TxFeature.java
@FeatureConfiguration
class TxFeature {
@Feature
public TxAnnotationDriven txAnnotationDriven(PlatformTransactionManager txManager) {
return new TxAnnotationDriven(txManager);
}
}
@FeatureConfiguration
classes are processed by the Spring container in expectation of @Feature
methods that return FeatureSpecifation
types such as TxAnnotationDriven
.
@FeatureConfiguration
classes are designed as a companion to @Configuration
classes. The two may be used interchangably when bootstrapping the container:
ApplicationContext ctx =
new AnnotationConfigApplicationContext(TransferServiceConfig.class, TxFeatures.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
and one may @Import
the other:
@Configuration
@Import(TxFeature.class)
public class TransferServiceConfig { ... }
or:
@FeatureConfiguration
@Import(TransferServiceConfig.class)
public class TxFeature { ... }
@FeatureConfiguration
classes may contain one or more @Feature
methods. For example, we could configure both component scanning and annotation-driven transaction management within the same class:
@FeatureConfiguration
class MyFeatures {
@Feature
public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
return new TxAnnotationDriven(txManager);
}
@Feature
public ComponentScanSpec scan() {
return new ComponentScanSpec("com.bank.service");
}
}
@Feature
methods are called by the container at bootstrap time and the FeatureSpecification
objects returned are processed in order to configure the actual internals of the feature in question. In the case of TxAnnotationDriven
, proxy creation is configured, a transaction interceptor bean is registered, and so on; in the case of ComponentScanSpec
, a ClassPathBeanDefinitionScanner
is configured with the values specified and then run in order to detect and register your classes marked with @Component
or other Spring stereotype annotations.
@Feature
methods may accept parameters in order to get references to Spring beans:
@Feature
public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
return new TxAnnotationDriven(txManager);
}
In order for the parameter to be supplied by the container, there must be a Spring bean of type PlatformTransactionManager
declared elsewhere, probably within a @Configuration
class. This notion of 'parameter autowiring' is not new to the framework; for example, this is similar to the way @InitBinder
and @RequestMapping
methods work in Spring MVC. The ability to refer to actual spring bean instances keeps things type-safe by avoiding the use of string-based bean names.
The FeatureSpecification
implementations shipped out of the box with the framework are designed for maximum ease of use when writing @Feature
methods. As you can see in the screenshot below, ComponentScanSpec
methods are chainable, and the public API of the class has been carefully designed to optimize the content-assist experience when working in your IDE:
The following FeatureSpecification implementations are already available with the first milestone release:
ComponentScanSpec
TxAnnotationDriven
MvcAnnotationDriven
MvcResources
MvcViewControllers
MvcDefaultServletHandler
You'll see many of the Mvc*
types put to use in Rossen's upcoming post in this series covering enhancements to Spring MVC in Spring 3.1 M1.
With the second milestone we'll add specification implementations for more of the most commonly used namespace elements such as context:mbean-export/
, aop:aspectj-autoproxy/
and so on.
Note that not all Spring XML namespace elements are alike. Some like those mentioned already are used to configure container features, while others like jee:jndi-lookup/
and everything in the util:*/
namespace are simply convenient utilities for doing in XML what can otherwise easily be done in code without any special support. For this reason, don't expect a one-to-one mapping between Spring XML elements and FeatureSpecification
types. Only those elements that make sense will be translated into code.
Just as users may define their own Spring XML namespaces and register their own NamespaceHandler
and BeanDefinitionParser
implementations, naturally the entire Feature*
model is just as open. I'll defer to the Javadoc for full details, but suffice it to say that you can author your own FeatureSpecification
types and use them with ease in @Feature
methods.
And for those that have already implemented custom namespaces and BeanDefinitionParsers
, you'll be interested to know that within the framework, we've refactored existing BeanDefinitionParsers
to delegate to FeatureSpecifications
internally. This greatly simplifies the implementation of a parser, and also ensures reuse of critical bean registration logic across the XML and @Configuration
styles. We encourage you to consider this same refactoring wherever appropriate.
While Spring continues to move forward with code-based configuration, it must be noted again and again that we are in no way eliminating, deprecating or otherwise encouraging users to phase out the use of XML to configure the container. Spring XML remains popular and many users are satisfied with it, especially considering the advanced tooling options that the SpringSource Tool Suite provides for authoring bean configuration files. @Configuration
-- and now @FeatureConfiguration
-- classes provide another first-class configuration approach for those that prefer to work in Java as much as possible. So rest assured that Spring XML isn't going anywhere -- indeed, with Spring 3.1 M1 we've given plenty of attention to XML with the addition of nested
elements, the c:
namespace for more convenient constructor injection, and the cache:
nampespace to configure Spring's new caching feature.
We hope you'll find FeatureSpecification
classes to be a natural, powerful extension of the existing @Configuration
class model. Take a look at the sample application accompanying this post, and please do provide feedback!