Spring 3.1: More Control, Less XML
By Chris Hardin, OCI Senior Software Engineer
December 2011
Introduction
The production release of Spring 3.1 is on the horizon and will bring many new framework features as well as extend the XML-free configuration capabilities that were introduced in Spring 3.0. The goal expressed by the developers of Spring is to make this newer Java-based style of configuration a first-class alternative to the traditional XML or annotation-based configuration styles.
The XML-based configuration has been evolving for many years and has a rich set of extensions through various schemas and namespaces. It will take time for all of that functionality to be made available via configuration classes. However, the Spring developers have shown their committment to that goal in Spring 3.1 by porting more existing functionality as well as providing Java-based implementations for all of the new 3.1 features.
Topics
- Reviewing the Goals of a Dependency-Injection Framework
- Examining the Three Configuration Styles
- XML
- Annotations
- Java-Based
- Exploring Java-Based Configuration Further
- Bean Scopes and Other Attributes
- Composition and Loading of Configuration Classes
- New Spring 3.1 Features
- Loading Spring in a Servlet 3.0 Container
- Environment and Profiles
- Setting the Active Profile
- Custom Profile Annotations
- Hibernate SessionFactoryBuilder APIs
- @EnableTransactionManagement
- Test Context Support for Configuration Classes and Profiles
- And Much, Much More...
- Summary
- References
Reviewing the Goals of a Dependency-Injection Framework
- Reduce Glue Code: Remove the need for every developer to write his own factory lookup code, and keep this type of plumbing code out of the core application.
- Externalize Dependencies: Keep all information about how components interact separate from the application code.
- Manage Dependencies in a Single Location: Make it easy to see how the system is configured without having to trace through application code.
- Improve Testability: Externalizing dependencies and programming to interfaces make it easy to test classes in isolation by injecting mock objects in place of dependencies.
- Change Implementations Without Modifying Application Code: Switching out implementations of a dependency should be a simple matter of modifying an external configuration without touching any application code.
Examining the Three Configuration Styles
XML
XML is the original Spring style, in which all configuration is contained in XML files. This style can achieve all the stated goals of a dependency-injection framework. In recent years, however, the popularity of XML has severly declined. Many see it as being too verbose and difficult to maintain. When used for dependency injection, it becomes increasingly difficult to trace through the connections between components as a system grows in size and complexity. SpringSource (the developer of Spring) provides sophisticated IDE plug-ins with tools for building the XML and displaying graphical representations of how all the components are connected. However, Java developers are most comfortable with Java and tracing through code; they don't always want to deal with a separate set of tools for their configuration.
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd">
-
- <bean id="greetingService" class="com.example.GreetingService"
- c:_0-ref="source" c:_1-ref="destination" ></bean>
-
- <bean id="source" class="com.example.GreetingSourceImpl"
- p:greeting="Hello World" ></bean>
-
- <bean id="destination" class="com.example.GreetingDestinationImpl"></bean>
-
- </beans>
The above code listing is an example of a Spring XML file that configures three components. The greetingService
component is injected with the source
and destination components using constructor injection. Note the use of the c
namespace to configure the constructor arguments for greetingService
. The c
namespace is a new convenience being added in Spring 3.1.
Annotations
Java 5 introduced the ability to annotate Java code with meta-data. Spring 2.5 took advantage of this new capability by providing a set of Spring annotations that could be placed directly on application classes as an alternative to configuring those classes in XML. Annotations do not provide all the capability of the XML-based configuration, but they allow fairly sophisticated configuration of dependencies. One drawback of this approach is that using Spring annotations couples application code to the Spring framework. However, in recent years, new Java standards (JSR 250 and JSR 330) have emerged to provide standard annotations that can be used instead of Spring-specific annotations, mitigating this concern.
While many developers find the ease of writing annotations to be a great relief, several problems still remain with this annotation-based approach. When held up against the stated goals of the framework, it fails to meet several of them. Dependencies are no longer externalized, nor are they managed in a single location. Examining the configuration involves sifting through application code, and tracing through the connections between components actually becomes even harder as these connections become more implicit. Additionally, application classes must now be modified directly in order to change implementations of dependencies. Some might argue that annotations are really meta-data and the actual application code is not being modified; however, configuration changes still require the modification and compilation of application class files, so some of the advantages of using an external XML-based approach have definitely been lost.
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- @Component
- public class GreetingService implements Service {
-
- private GreetingSource source;
- private GreetingDestination destination;
-
- @Autowired
- public GreetingService(GreetingSource source, GreetingDestination destination){
- this.source = source;
- this.destination = destination;
- }
-
- public void execute() {
- destination.write(source.getGreeting());
- }
- }
In the listing above, the GreetingService
component is configured using annotations instead of XML. Elsewhere in the code, the implementations for GreetingSource
and GreetingDestination
have also been configured using annotations. The @Autowired
annotation will cause Spring to automatically construct a GreetingService
object using any implementations of GreetingSource
and GreetingDestination
that it finds. This configuration looks much simpler than the XML; it requires just two Spring annotations to be added to the application class and Spring does the rest. The problem is that simple traceability has been lost. Without using a special Spring tool to analyze the code, the only way to see which beans will get injected is to search through the code for annotated implementations. Likewise, changing the implementations requires removing annotations from one application class and putting them on another.
Java-Based
Introduced in Spring 3.0, Java-based configuration provides a new alternative that retains all the benefits of the XML-based approach while solving many of its problems. With this style, all configuration is done in plain Java code. This code is contained in configuration classes, which are completely separate from the application code. These configuration classes act like internal bean factories, supplying the Spring container with fully configured application objects.
At first glance, it seems like Spring has given up on the goals of the framework and gone back to hard-wiring everything together in code. A configuration class contains plain Java code, in which each application component is directly instantiated and then injected using either constructor parameters or by directly calling setter methods. The developer is once again responsible for writing code to cover every aspect of creating and configuring each application component. However, when holding this style up against the stated goals of the framework, it is important to understand the distinction between application and configuration code. By keeping the configuration code completely separate from the application code, this approach has all of the same advantages that were afforded by the XML approach. It has simply exchanged an XML file for a Java file. They have the same function and role in configuring the Spring container; they just use a different syntax. All configuration is still external to the application code and can be modified and compiled separately.
The advantages over the other configuration styles are many:
- Freedom: The biggest advantage is the complete freedom the developer has in writing the configuration. When configuring a component, the developer can do just about anything in the code, such as calling on utility methods, using other types of builders or factories, or pulling values from properties or other resources. This makes it very easy to integrate any type of legacy code or different instantiation methods into a Spring configuration.
- Compile-time Checking: Developers can stop wasting time debugging typos in XML configurations that are discovered at runtime.
- IDE Support: Standard Java IDE features can be used to get content-assistance and traceability without needing to install special Spring or XML-related tools and plug-ins.
- Traceability: Navigation through the connections being made in configuration code is no different than navigation through application code. Connections are explicit and immediately obvious when looking at the configuration.
- No Special Syntax/Tags to Learn: With the exception of a few special annotations used on the configuration classes, the Spring configuration is now plain old Java.
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- public class BeanConfig {
-
- @Bean
- public GreetingSource source(){
- GreetingSourceImpl s = new GreetingSourceImpl();
- s.setGreeting("Hello World");
- return s;
- }
-
- @Bean
- public GreetingDestination destination(){
- return new GreetingDestinationImpl();
- }
-
- @Bean
- public Service greetingService(){
- return new GreetingService(source(), destination());
- }
- }
The listing above shows the complete Java-based configuration for the GreetingService
example. The @Configuration
annotation identifies the class as a source of Spring beans. Each @Bean
annotated method produces one bean to be managed by the Spring container. When compared to the equivalent XML configuration shown earlier, each @Bean
method corresponds to a tag in the XML. As a good practice, each method returns an interface type. The implementation choice is made in the body of the method, where an implementation is constructed and fully injected before being returned. The
greetingService
method provides an example of how to inject other Spring beans. Beans are retrieved by calling the corresponding methods on the configuration class and then passed to the GreetingService
constructor. By default, the name of the method becomes the name of the bean that it returns.
Glancing at this configuration class, it is immediately obvious how the components are wired together. Even if the configuration is split across multiple classes, a simple ctrl-click on the name of a bean method will quickly bring up its implementation in most IDEs.
Exploring Java-Based Configuration Further
Bean Scopes and Other Attributes
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Scope;
-
- @Configuration
- public class BeanConfig {
-
- @Bean(name = {"source", "greetingSource", "bob"}, destroyMethod="destroy")
- public GreetingSource source(){
- GreetingSourceImpl s = new GreetingSourceImpl();
- s.setGreeting("Hello World");
- s.init();
- return s;
- }
-
- @Bean(name="consoleDestination", initMethod="init")
- @Scope("prototype")
- public GreetingDestination destination(){
- return new GreetingDestinationImpl();
- }
-
- @Bean
- public Service greetingService(){
- return new GreetingService(source(), destination());
- }
- }
The @Bean
annotation has attributes similar to those found in the XML tag. These attributes can be used to specify names and life-cycle methods for the bean. Note that initialization methods can also be called directly in the bean method, as shown in the
source
method above.
The @Scope
annotation allows the specification of a lifecycle scope for the bean. By default, all beans are singletons, meaning that the Spring container instantiates exactly one instance of the bean and injects that same instance everywhere that bean is needed. A bean method that is annotated with @Scope("prototype")
will have prototype
scope, meaning that Spring will construct a new instance every time the bean is requested or needed for injection. Additional scopes are available for use in Web applications— request
, session
, and globalsession
.
At first glance, it is not obvious how Spring is going to respect singleton scope using Java configuration classes. In the example above, every time a GreetingSource
bean is needed, the source
method is called and appears to instantiate a new object. However, at runtime, the Spring container creates a CGLib proxy for each of the configuration classes. Every time the source
method is called, the proxy first checks the Spring container to see if an instance of the source
bean already exists. If it does exist, then the proxy simply returns it and the real source
method is never called. If the proxy doesn't find an existing instance of the bean, it forwards the call to the real source
method, which instantiates and returns a new bean. The proxy caches this bean in the Spring container before returning it to the caller. In order for Spring to proxy the configuration class, it must have a no-argument constructor and must not be declared as final
.
Composition and Loading of Configuration Classes
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- public class BeanConfig {
-
- @Bean
- public GreetingSource source(){
- GreetingSourceImpl s = new GreetingSourceImpl();
- s.setGreeting("Hello World");
- return s;
- }
-
- @Bean
- public GreetingDestination destination(){
- return new GreetingDestinationImpl();
- }
- }
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Import;
-
- @Configuration
- @Import(BeanConfig.class)
- public class ServiceConfig {
-
- @Autowired
- private BeanConfig beanConfig;
-
- @Bean
- public Service greetingService(){
- return new GreetingService(beanConfig.source(),
- beanConfig.destination());
- }
- }
The previous BeanConfig
example has now been split into two separate configuration classes— BeanConfig
and ServiceConfig
. ServiceConfig
contains the method for the greetingService
bean and needs to access the source
and destination
methods from the BeanConfig
class in order to inject those beans into the greetingService
.
Configuration classes are themselves loaded into the Spring container as singleton beans. This means that Spring annotations can be used to configure them for injection. In the example above, the ServiceConfig
class has a private BeanConfig
field that is configured with an @Autowired
annotation. When the Spring container loads, the BeanConfig
singleton instance will be injected into the field in ServiceConfig
. This allows the greetingService
method to easily call the source
and destination
methods to retrieve the beans needed to configure the greetingService
bean.
This example shows how the configuration can be modularized into multiple classes, without sacrificing the ability to see at a glance where beans are defined. It is immediately obvious that the source
and destination
beans are configured in the BeanConfig
class and that configuration is a ctrl-click away.
The @Import(BeanConfig.class)
annotation on the ServiceConfig
class ensures that the BeanConfig
class is automatically loaded by Spring whenever the ServiceConfig
class is loaded. The listing below shows an example of loading the Spring container by supplying just the ServiceConfig
class as a parameter.
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- public class HelloWorld {
-
- public static void main(String[] args) {
-
- //Construct the Spring container
- AnnotationConfigApplicationContext ac =
- new AnnotationConfigApplicationContext(ServiceConfig.class);
-
- //Retrieve and use the greetingService bean
- Service service = ac.getBean("greetingService", Service.class);
- service.execute();
- }
- }
AnnotationConfigApplicationContext
is an implementation of the Spring container that specifically works with Java or annotation-based configuration. The above listing is an example of loading the container by passing configuration classes to the container's constructor. It is not necessary to pass the BeanConfig
class, because it was imported into ServiceConfig
.
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- public class HelloWorld {
-
- public static void main(String[] args) {
-
- AnnotationConfigApplicationContext ac =
- new AnnotationConfigApplicationContext();
-
- //Scan a package containing configuration classes
- ac.scan("com.example.config");
-
- //Call refresh exactly once after configuring the container
- ac.refresh();
-
- Service service = ac.getBean("greetingService", Service.class);
- service.execute();
- }
- }
In a large application, it may be simpler to scan one or more packages to automatically find configuration classes instead of listing them explicitly when constructing the Spring container. The above listing shows the use of the container's scan
and refresh
methods to load all configuration classes in the com.example.config
package.
New Spring 3.1 Features
Loading Spring in a Servlet 3.0 Container
- import javax.servlet.ServletContext;
- import org.springframework.web.WebApplicationInitializer;
- import org.springframework.web.context.ContextLoaderListener;
- import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
-
- public class HelloWorldWebApplicationInitializer implements WebApplicationInitializer {
-
- @Override
- public void onStartup(ServletContext container) {
-
- //Construct a Web-specific version of the Spring container
- AnnotationConfigWebApplicationContext rootContext =
- new AnnotationConfigWebApplicationContext();
-
- ac.scan("com.example.config");
-
- container.addListener(new ContextLoaderListener(rootContext));
- }
- }
The Servlet 3.0 specification goes a step further in removing XML from Web applications by allowing initialization code to be loaded instead of the traditional web.xml file. Spring 3.1 provides a new Spring class called SpringServletContainerInitializer
, which will be bootstrapped automatically by any Servlet 3.0 container as part of this new code-based approach to servlet container configuration. SpringServletContainerInitializer
searches the classpath of the Web application looking for any implementations of the Spring interface WebApplicationInitializer
.
Instead of being configured in a Web.xml file, the Spring container can be constructed programmatically in an implementation of WebApplicationInitializer
. The listing above shows an implementation that creates a Spring container using the scan
method to find configuration classes. This Spring container must be passed to the constructor of a ContextLoaderListener
, which must then be added as a listener to the servlet container. The ContextLoaderListener
will finish the initialization of the Spring container, calling refresh
as necessary, and tie its lifecycle to that of the servlet container.
Environment and Profiles
Environment
is a new abstraction that allows specific configurations to be loaded in different deployment environments, without modifying Spring configuration files or code. The Environment
has a set of active profiles that determine which bean configurations load. Each XML element, annotated application class, or configuration class can be assigned to zero or more profiles. If none of their assigned profiles are active, they are ignored by Spring. If they are not assigned any profiles, they are always loaded.
- import javax.sql.DataSource;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Profile;
- import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
- import org.springframework.util.ResourceUtils;
-
- @Configuration
- public class DataConfig {
-
- //The datasource that is injected will depend on the active profile
- @Autowired
- private DataSource datasource;
-
- ...//Additional code using the datasource has been omitted for now
-
- //Define a datasource for the dev environment
- @Profile("dev")
- @Configuration
- public static class DataConfigDev {
-
- @Bean(destroyMethod="shutdown")
- public DataSource dataSource(){
- //Create an embedded test database
- //The EmbeddedDatabaseBuilder was added in Spring 3.0 and is convenient for testing
- return new EmbeddedDatabaseBuilder().
- addScript(ResourceUtils.CLASSPATH_URL_PREFIX + "sql/schema.sql").
- addScript(ResourceUtils.CLASSPATH_URL_PREFIX + "sql/test-data.sql").
- build();
- }
- }
-
- //Define a datasource for the prod environment
- @Profile("prod")
- @Configuration
- public static class DataConfigProd {
-
- @Bean
- public DataSource dataSource(){
- ...//Construct a real datasource or return one from a JNDI lookup
- }
- }
- }
-
Because the @Profile
annotation can only be applied to classes, it is often convenient to nest inner configuration classes inside of outer ones. The ability to nest configuration classes is a new feature in Spring 3.1. In the listing above, the DataConfig
class, which will eventually contain the Hibernate configuration, has two nested configuration classes- DataConfigDev
and DataConfigProd
. They both contain a dataSource
method that supplies a DataSource
bean. Because each class has a different @Profile
annotation, the correct DataSource
for the current environment will be instantiated and automatically injected into a field in the DataConfig
class. A later example will show how the datasource
field will be used to construct a Hibernate SessionFactory
.
Setting the Active Profile
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- public class Main {
-
- public static void main(String[] args) {
-
- AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
-
- //Get the environment from the container and set the active profiles
- ac.getEnvironment().setActiveProfiles("dev", "foo");
-
- ac.scan("media.config");
- ac.refresh();
-
- MediaService mediaService = ac.getBean("mediaService", MediaService.class);
-
- mediaService.printCDs();
- }
-
- }
The active profiles can be set programmatically by calling setActiveProfiles
on the Environment
.
-Dspring.profiles.active="foo,dev"
The active profiles can also be set using a system property.
- <context-param>
- <param-name>spring.profiles.active</param-name>
- <param-value>foo,dev</param-value>
- </context-param>
In a Web application using a traditional web.xml file and the ContextLoaderListener
to load Spring configuration, the active profiles can be set using a special context parameter.
Custom Profile Annotations
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- import org.springframework.context.annotation.Profile;
-
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Profile("dev")
- public @interface Dev {
- }
The @Profile
annotation can be used to meta-annotate custom annotations. In the example above, a new @Dev
annotation is created, which can be used to annotate bean configurations instead of using @Profile("dev")
. Creating custom profile annotations avoids problems with typos or inconsistencies in profile names across multiple configuration classes.
Hibernate SessionFactoryBuilder APIs
- import java.util.Properties;
- import javax.sql.DataSource;
- import org.hibernate.SessionFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Profile;
- import org.springframework.core.io.Resource;
- import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
- import org.springframework.orm.hibernate3.HibernateTransactionManager;
- import org.springframework.orm.hibernate3.SessionFactoryBuilder;
- import org.springframework.transaction.PlatformTransactionManager;
- import org.springframework.transaction.annotation.EnableTransactionManagement;
- import org.springframework.util.ResourceUtils;
-
- @Configuration
- @EnableTransactionManagement
- public class DataConfig {
-
- @Autowired
- private ApplicationContext ctx;
-
- @Autowired
- private DataSource datasource;
-
- @Bean
- public SessionFactory sessionFactory() throws Exception {
- //Get directory resource for Hibernate mapping files
- Resource location =
- ctx.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "hibernate");
-
- //Hibernate configuration properties
- Properties hProps = new Properties();
- hProps.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
-
- //Use the SessionFactoryBuilder to create a Hibernate SessionFactory
- return new SessionFactoryBuilder(datasource)
- .setMappingDirectoryLocations(location)
- .setHibernateProperties(hProps)
- .buildSessionFactory();
- }
-
- @Bean
- public PlatformTransactionManager transactionManager() throws Exception{
- //Create a transaction manager for the Hibernate sessions
- return new HibernateTransactionManager(sessionFactory());
- }
-
- @Bean
- public MediaDAO mediaDAO() throws Exception{
- //Inject the Hibernate SessionFactory into a Data Access Object
- return new SimpleMediaDAO(sessionFactory());
- }
-
- ...//DataConfigDev and DataConfigProd have been omitted
- }
-
Configuring a Hibernate SessionFactory
is a common task in Spring applications. Spring 3.1 adds a new SessionFactoryBuilder
class that makes it easy to configure a SessionFactory
programmatically instead of using the traditional XML.
@EnableTransactionManagement
- @Configuration
- @EnableTransactionManagement
- public class DataConfig {
- ...//Class contents have been omitted
- }
Several of the Spring XML namespaces provide simple tags that turn on different functions of the framework. Spring 3.1 introduces some new annotations that serve as code-based equivalents to some of these commonly used tags. One example is the @EnableTransactionManagement
annotation shown in the listing above. It is equivalent to the tag from the transaction namespace. Both the tag and the annotation activate processing of transaction-related annotations on application classes.
Test Context Support for Configuration Classes and Profiles
- import media.service.MediaService;
- import media.service.SimpleMediaService;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Import;
-
- @Configuration
- @Import(DataConfig.class)
- public class ServiceConfig {
-
- @Autowired
- private DataConfig dataConfig;
-
- @Bean
- public MediaService mediaService() throws Exception{
- return new SimpleMediaService(dataConfig.mediaDAO());
- }
- }
-
The listing above shows another configuration class, ServiceConfig
, which imports theDataConfig
class and uses the mediaDAO
bean it provides to construct a mediaService
bean. The mediaService
bean will be used in the unit test example below.
- import static org.junit.Assert.assertEquals;
- import media.Media;
- import media.config.ServiceConfig;
-
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.test.context.ActiveProfiles;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- import org.springframework.test.context.support.AnnotationConfigContextLoader;
-
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes={ServiceConfig.class})
- @ActiveProfiles("dev")
- public class ServiceTest {
-
- @Autowired
- private MediaService mediaService;
-
- @Test
- public void testMediaService() {
- Media a = mediaService.addCD("Foo", "Rock", "Joe");
- Media r = mediaService.retrieveMedia(a.getId());
- assertEquals(a.getId(), r.getId());
- assertEquals(a.getTitle(), r.getTitle());
- }
- }
-
The @ContextConfiguration
annotation allows a test to run with a Spring container loaded from configuration classes. @ActiveProfiles
is a new annotation that activates a set of profiles for a test run. In the listing above, the active profile is being set to "dev," and the ServiceConfig
class is being loaded into a Spring container. This allows the test to use a MediaService
bean to interact with the embedded test database created in the DataConfigDev
class.
And Much, Much More...
The previous sections have barely scratched the surface of the new features available in Spring 3.1. For a more complete list and additional resources, see the "New Features and Enhancements in Spring 3.1" section of the Spring 3.1 reference documentation.
Summary
Java-based configuration is an excellent choice going forward for both old and new Spring applications. It retains all the benefits of XML-based configuration while overcoming many of its shortcomings. It also presents an easier learning curve for Java developers new to Spring. New Spring 3.1 features such as profiles make it even more powerful and easier to support deployments in multiple environments.
References
- [1] SpringSource team blog posts related to version 3.1
http://blog.springsource.com/category/spring/31/ - [2] Spring 3.1 RC1 Reference Documentation
http://static.springsource.org/spring/docs/3.1.0.RC1/spring-framework-reference/html/new-in-3.1.html - [3] Spring 3.1 RC1 API
http://static.springsource.org/spring/docs/3.1.0.RC1/javadoc-api/