Spring 3.1: More Control, Less XML

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

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.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xmlns:c="http://www.springframework.org/schema/c"
  6. xmlns:context="http://www.springframework.org/schema/context"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context.xsd">
  11.  
  12. <bean id="greetingService" class="com.example.GreetingService"
  13. c:_0-ref="source" c:_1-ref="destination" ></bean>
  14.  
  15. <bean id="source" class="com.example.GreetingSourceImpl"
  16. p:greeting="Hello World" ></bean>
  17.  
  18. <bean id="destination" class="com.example.GreetingDestinationImpl"></bean>
  19.  
  20. </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.

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.stereotype.Component;
  3.  
  4. @Component
  5. public class GreetingService implements Service {
  6.  
  7. private GreetingSource source;
  8. private GreetingDestination destination;
  9.  
  10. @Autowired
  11. public GreetingService(GreetingSource source, GreetingDestination destination){
  12. this.source = source;
  13. this.destination = destination;
  14. }
  15.  
  16. public void execute() {
  17. destination.write(source.getGreeting());
  18. }
  19. }

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:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3.  
  4. @Configuration
  5. public class BeanConfig {
  6.  
  7. @Bean
  8. public GreetingSource source(){
  9. GreetingSourceImpl s = new GreetingSourceImpl();
  10. s.setGreeting("Hello World");
  11. return s;
  12. }
  13.  
  14. @Bean
  15. public GreetingDestination destination(){
  16. return new GreetingDestinationImpl();
  17. }
  18.  
  19. @Bean
  20. public Service greetingService(){
  21. return new GreetingService(source(), destination());
  22. }
  23. }

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

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.context.annotation.Scope;
  4.  
  5. @Configuration
  6. public class BeanConfig {
  7.  
  8. @Bean(name = {"source", "greetingSource", "bob"}, destroyMethod="destroy")
  9. public GreetingSource source(){
  10. GreetingSourceImpl s = new GreetingSourceImpl();
  11. s.setGreeting("Hello World");
  12. s.init();
  13. return s;
  14. }
  15.  
  16. @Bean(name="consoleDestination", initMethod="init")
  17. @Scope("prototype")
  18. public GreetingDestination destination(){
  19. return new GreetingDestinationImpl();
  20. }
  21.  
  22. @Bean
  23. public Service greetingService(){
  24. return new GreetingService(source(), destination());
  25. }
  26. }

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

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3.  
  4. @Configuration
  5. public class BeanConfig {
  6.  
  7. @Bean
  8. public GreetingSource source(){
  9. GreetingSourceImpl s = new GreetingSourceImpl();
  10. s.setGreeting("Hello World");
  11. return s;
  12. }
  13.  
  14. @Bean
  15. public GreetingDestination destination(){
  16. return new GreetingDestinationImpl();
  17. }
  18. }
  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5.  
  6. @Configuration
  7. @Import(BeanConfig.class)
  8. public class ServiceConfig {
  9.  
  10. @Autowired
  11. private BeanConfig beanConfig;
  12.  
  13. @Bean
  14. public Service greetingService(){
  15. return new GreetingService(beanConfig.source(),
  16. beanConfig.destination());
  17. }
  18. }

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.

  1. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  2.  
  3. public class HelloWorld {
  4.  
  5. public static void main(String[] args) {
  6.  
  7. //Construct the Spring container
  8. AnnotationConfigApplicationContext ac =
  9. new AnnotationConfigApplicationContext(ServiceConfig.class);
  10.  
  11. //Retrieve and use the greetingService bean
  12. Service service = ac.getBean("greetingService", Service.class);
  13. service.execute();
  14. }
  15. }

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.

  1. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  2.  
  3. public class HelloWorld {
  4.  
  5. public static void main(String[] args) {
  6.  
  7. AnnotationConfigApplicationContext ac =
  8. new AnnotationConfigApplicationContext();
  9.  
  10. //Scan a package containing configuration classes
  11. ac.scan("com.example.config");
  12.  
  13. //Call refresh exactly once after configuring the container
  14. ac.refresh();
  15.  
  16. Service service = ac.getBean("greetingService", Service.class);
  17. service.execute();
  18. }
  19. }

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

  1. import javax.servlet.ServletContext;
  2. import org.springframework.web.WebApplicationInitializer;
  3. import org.springframework.web.context.ContextLoaderListener;
  4. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
  5.  
  6. public class HelloWorldWebApplicationInitializer implements WebApplicationInitializer {
  7.  
  8. @Override
  9. public void onStartup(ServletContext container) {
  10.  
  11. //Construct a Web-specific version of the Spring container
  12. AnnotationConfigWebApplicationContext rootContext =
  13. new AnnotationConfigWebApplicationContext();
  14.  
  15. ac.scan("com.example.config");
  16.  
  17. container.addListener(new ContextLoaderListener(rootContext));
  18. }
  19. }

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.

  1. import javax.sql.DataSource;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.context.annotation.Profile;
  6. import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
  7. import org.springframework.util.ResourceUtils;
  8.  
  9. @Configuration
  10. public class DataConfig {
  11.  
  12. //The datasource that is injected will depend on the active profile
  13. @Autowired
  14. private DataSource datasource;
  15.  
  16. ...//Additional code using the datasource has been omitted for now
  17.  
  18. //Define a datasource for the dev environment
  19. @Profile("dev")
  20. @Configuration
  21. public static class DataConfigDev {
  22.  
  23. @Bean(destroyMethod="shutdown")
  24. public DataSource dataSource(){
  25. //Create an embedded test database
  26. //The EmbeddedDatabaseBuilder was added in Spring 3.0 and is convenient for testing
  27. return new EmbeddedDatabaseBuilder().
  28. addScript(ResourceUtils.CLASSPATH_URL_PREFIX + "sql/schema.sql").
  29. addScript(ResourceUtils.CLASSPATH_URL_PREFIX + "sql/test-data.sql").
  30. build();
  31. }
  32. }
  33.  
  34. //Define a datasource for the prod environment
  35. @Profile("prod")
  36. @Configuration
  37. public static class DataConfigProd {
  38.  
  39. @Bean
  40. public DataSource dataSource(){
  41. ...//Construct a real datasource or return one from a JNDI lookup
  42. }
  43. }
  44. }
  45.  

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

  1. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  2.  
  3. public class Main {
  4.  
  5. public static void main(String[] args) {
  6.  
  7. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  8.  
  9. //Get the environment from the container and set the active profiles
  10. ac.getEnvironment().setActiveProfiles("dev", "foo");
  11.  
  12. ac.scan("media.config");
  13. ac.refresh();
  14.  
  15. MediaService mediaService = ac.getBean("mediaService", MediaService.class);
  16.  
  17. mediaService.printCDs();
  18. }
  19.  
  20. }

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.

  1. <context-param>
  2. <param-name>spring.profiles.active</param-name>
  3. <param-value>foo,dev</param-value>
  4. </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

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5.  
  6. import org.springframework.context.annotation.Profile;
  7.  
  8. @Target(ElementType.TYPE)
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Profile("dev")
  11. public @interface Dev {
  12. }

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

  1. import java.util.Properties;
  2. import javax.sql.DataSource;
  3. import org.hibernate.SessionFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.Profile;
  9. import org.springframework.core.io.Resource;
  10. import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
  11. import org.springframework.orm.hibernate3.HibernateTransactionManager;
  12. import org.springframework.orm.hibernate3.SessionFactoryBuilder;
  13. import org.springframework.transaction.PlatformTransactionManager;
  14. import org.springframework.transaction.annotation.EnableTransactionManagement;
  15. import org.springframework.util.ResourceUtils;
  16.  
  17. @Configuration
  18. @EnableTransactionManagement
  19. public class DataConfig {
  20.  
  21. @Autowired
  22. private ApplicationContext ctx;
  23.  
  24. @Autowired
  25. private DataSource datasource;
  26.  
  27. @Bean
  28. public SessionFactory sessionFactory() throws Exception {
  29. //Get directory resource for Hibernate mapping files
  30. Resource location =
  31. ctx.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "hibernate");
  32.  
  33. //Hibernate configuration properties
  34. Properties hProps = new Properties();
  35. hProps.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
  36.  
  37. //Use the SessionFactoryBuilder to create a Hibernate SessionFactory
  38. return new SessionFactoryBuilder(datasource)
  39. .setMappingDirectoryLocations(location)
  40. .setHibernateProperties(hProps)
  41. .buildSessionFactory();
  42. }
  43.  
  44. @Bean
  45. public PlatformTransactionManager transactionManager() throws Exception{
  46. //Create a transaction manager for the Hibernate sessions
  47. return new HibernateTransactionManager(sessionFactory());
  48. }
  49.  
  50. @Bean
  51. public MediaDAO mediaDAO() throws Exception{
  52. //Inject the Hibernate SessionFactory into a Data Access Object
  53. return new SimpleMediaDAO(sessionFactory());
  54. }
  55.  
  56. ...//DataConfigDev and DataConfigProd have been omitted
  57. }
  58.  

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

 
  1. @Configuration
  2. @EnableTransactionManagement
  3. public class DataConfig {
  4. ...//Class contents have been omitted
  5. }

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

  1. import media.service.MediaService;
  2. import media.service.SimpleMediaService;
  3.  
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.context.annotation.Import;
  8.  
  9. @Configuration
  10. @Import(DataConfig.class)
  11. public class ServiceConfig {
  12.  
  13. @Autowired
  14. private DataConfig dataConfig;
  15.  
  16. @Bean
  17. public MediaService mediaService() throws Exception{
  18. return new SimpleMediaService(dataConfig.mediaDAO());
  19. }
  20. }
  21.  

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.

  1. import static org.junit.Assert.assertEquals;
  2. import media.Media;
  3. import media.config.ServiceConfig;
  4.  
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.test.context.ActiveProfiles;
  9. import org.springframework.test.context.ContextConfiguration;
  10. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  11. import org.springframework.test.context.support.AnnotationConfigContextLoader;
  12.  
  13. @RunWith(SpringJUnit4ClassRunner.class)
  14. @ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes={ServiceConfig.class})
  15. @ActiveProfiles("dev")
  16. public class ServiceTest {
  17.  
  18. @Autowired
  19. private MediaService mediaService;
  20.  
  21. @Test
  22. public void testMediaService() {
  23. Media a = mediaService.addCD("Foo", "Rock", "Joe");
  24. Media r = mediaService.retrieveMedia(a.getId());
  25. assertEquals(a.getId(), r.getId());
  26. assertEquals(a.getTitle(), r.getTitle());
  27. }
  28. }
  29.  

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