Spring JavaConfig Reference Guide

Rod Johnson

Costin Leau

Chris Beams

Spring Java Configuration

Version 1.0.0.M4

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

November 2008


Table of Contents

Preface
1. Introduction
1.1. What this guide covers
1.1.1. Topics not covered
1.2. What JavaConfig requires to run
1.3. Where to get support
1.4. Where to follow development
1.5. How to retrieve and build JavaConfig from source
1.6. How to import JavaConfig as an Eclipse project
1.7. How to obtain milestone builds
1.7.1. Via manual download
1.7.2. Via Maven
1.7.3. Via Ivy
1.8. How to obtain nightly (aka 'snapshot') builds
1.8.1. Via manual download
1.8.2. Via Maven
1.8.3. Via Ivy
2. Authoring @Configuration classes
2.1. @Configuration
2.2. @Bean
2.2.1. Declaring a bean
2.2.2. Injecting dependencies
2.2.3. Receiving lifecycle callbacks
2.2.3.1. Using JSR-250 annotations
2.2.3.2. Using Spring interfaces
2.2.3.3. Using @Bean initMethodName / destroyMethodName attributes
2.2.4. Using *Aware interfaces
2.2.5. Specifying bean scope
2.2.5.1. Using @Bean's scope attribute
2.2.5.2. @ScopedProxy
2.2.5.3. Lookup method injection
2.2.6. Customizing bean naming
2.2.7. Working with Spring FactoryBean implementations
3. Using @Configuration classes
3.1. Bootstrapping applications with JavaConfigApplicationContext
3.1.1. Construction Options
3.1.1.1. Construction by class literal
3.1.1.2. Construction by base package
3.1.1.3. Post-construction configuration
3.1.2. Accessing beans with getBean()
3.1.2.1. Type-safe access
3.1.2.2. String-based access
3.2. Bootstrapping web applications with JavaConfigWebApplicationContext
4. Modularizing configurations
4.1. Partitioning bean definitions into multiple @Configuration classes
4.2. Referencing beans across @Configuration classes
4.2.1. Direct bean references with @Autowired
4.2.2. Fully-qualified bean references with @Autowired
4.3. Aggregating @Configuration classes with @Import
4.4. ConfigurationSupport
5. Working with externalized values
5.1. @ExternalValue
5.1.1. @ExternalValue fields
5.1.2. @ExternalValue methods
5.2. Available ValueSource annotations
6. Combining configuration styles
6.1. JavaConfig and XML
6.1.1. Bootstrapping JavaConfig from XML with ConfigurationPostProcessor
6.1.1.1. Configuring configurations
6.1.2. Bootstrapping XML from JavaConfig with @ImportXml
6.2. JavaConfig and Annotation-Driven Configuration
6.2.1. @AnnotationDrivenConfig
6.2.2. @ComponentScan
7. Transaction-management support
7.1. @AnnotationDrivenTx
8. AOP support
8.1. @AspectJAutoProxy
8.1.1. @Aspect-annotated @Configuration classes
9. JMX support
9.1. @MBeanExport
10. Testing support
10.1. JavaConfigContextLoader
11. Extending JavaConfig
11.1. @Plugin

Preface

Spring Java Configuration (JavaConfig) provides a pure-Java means of configuring the Spring IoC container. By taking advantage of Java 5.0 language features such as annotations and generics, JavaConfig allows users to express configuration logic and metadata directly in code, alleviating any need for XML.

By relying only on basic Java syntax and language features, JavaConfig offers several distinct advantages:

  • JavaConfig provides a truly object-oriented mechanism for dependency injection, meaning you can take full advantage of reuse, inheritance and polymorphism in your configuration code.
  • You are given complete control over instantiation and dependency injection, meaning that even the most complex objects can be dealt with gracefully.
  • Because only Java is required, you are left with fully refactorable configuration logic that requires no special tooling beyond your IDE.

To get a sense of how to use JavaConfig, let's configure an application consisting of two beans:

@Configuration
public class AppConfig {
    @Bean
    public Service service() {
        return new ServiceImpl(repository());
    }

    @Bean
    public Repository repository() {
        return new JdbcRepository();
    }
}

Bootstrapping this application would then be as simple as the following:

public class AppBootstrap {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AppConfig.class);
        Service service = ctx.getBean(Service.class);
        service.doSomething();
    }
}

While this example is a trivial one, JavaConfig can flex to meet the needs of the most complex and sophisticated enterprise applications. This guide will show you how.

1. Introduction

1.1. What this guide covers

This guide covers all aspects of Spring JavaConfig. It covers implementing and using @Configuration classes to configure the Spring IoC container and working with the feature set. It also covers extending and customizing JavaConfig.

1.1.1. Topics not covered

For the purposes of this document, a general familiarity with the core concepts of the Spring IoC container are assumed. It is beyond the scope of this document to discuss such matters at length. If the following concepts are not familiar, it is recommended that you first read Chapter 3. IoC from the core Spring Framework documentation (or that you reference its relevant sections on an as-needed basis while reading this document): inversion of control (IoC) and/or dependency injection (DI), spring-managed beans, bean scoping, autowiring, BeanFactory, ApplicationContext, BeanFactoryPostProcessor , BeanPostProcessor, Spring's initialization, destruction, and other bean lifecycle callback mechanisms.

If general aspect oriented programming (AOP) concepts are unfamiliar, or AOP with Spring and AspectJ 5's @Aspect style are unfamiliar, it is recommended that you first read Chapter 6. AOP from the Core Spring Framework documentation.

1.2. What JavaConfig requires to run

  • Java 5.0 or higher

  • Spring 2.5.6 or higher

  • AspectJ 1.6.2 or higher

  • CGLIB 2.1.3

1.3. Where to get support

Professional from-the-source support for Spring JavaConfig is available from SpringSource, the company behind Spring.

1.4. Where to follow development

You can provide feedback and help make JavaConfig best serve the needs of the Spring community by interacting with the developers at the Spring JavaConfig Community Forum.

Report bugs and influence the JavaConfig project roadmap using Spring's JIRA issue tracker.

Browse JavaConfig sources and subscribe to RSS commit feeds using the Spring's FishEye service.

Stay tuned to Continuous Integration and nightly snapshot build status using Spring's Bamboo service.

Subscribe to the Spring Community Portal and SpringSource Team Blog for the latest Spring news and announcements, including information on Spring JavaConfig releases.

Follow JavaConfig development, get release notifications and provide feedback to the JavaConfig team via Twitter .

1.5. How to retrieve and build JavaConfig from source

You must have Java 5.0 (or better) and Ant 1.7.0 (or better) installed to build JavaConfig from source.

svn co https://src.springframework.org/svn/spring-javaconfig/trunk/ spring-javaconfig
cd spring-javaconfig/org.springframework.config.java
ant clean test
            

.1. Publishing JavaConfig artifacts to a local Maven repository

ant jar publish-maven-local
            

1.6. How to import JavaConfig as an Eclipse project

The JavaConfig source tree contains Eclipse project metadata (.classpath and .project files), making it convienient to import. Also, as Ivy is used to resolve dependencies, it is necessary to set up an IVY_CACHE variable as detailed below.

From within Eclipse:
1. Preferences->Java->Build Path->Classpath Variables->New...
   Name:  IVY_CACHE
   Value: [your-javaconfig-root]>/ivy-cache/repository
2. File->Import->Existing Projects into Workspace
3. Select [your-javaconfig-root]/org.springframework.config.java/
4. org.springframework.config.java should show up with a checkbox
            

The project should now be imported, error-free and ready for development.

1.7. How to obtain milestone builds

1.7.1. Via manual download

Milestone builds are available from Spring's milestone build area.

1.7.2. Via Maven

To access milestone builds using Maven, add the following repositories to your Maven pom:

<repository>
    <id>com.springsource.repository.bundles.milestone</id>
    <name>SpringSource Enterprise Bundle Repository - SpringSource Milestone Releases</name>
    <url>http://repository.springsource.com/maven/bundles/milestone</url>
</repository>

<repository>
    <id>com.springsource.repository.bundles.external</id>
    <name>SpringSource Enterprise Bundle Repository - External Releases</name>
    <url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
                

Then add the following dependency:

<dependency>
    <groupId>org.springframework.javaconfig</groupId>
    <artifactId>org.springframework.config.java</artifactId>
    <version>1.0.0.M4</version>
</dependency>
                

1.7.3. Via Ivy

To access milestone builds using Ivy, add the following repositories to your Ivy config:

<url name="com.springsource.repository.bundles.milestone">
    <ivy pattern="http://repository.springsource.com/ivy/bundles/milestone/
                  [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
    <artifact pattern="http://repository.springsource.com/ivy/bundles/milestone/
                       [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
</url>

<url name="com.springsource.repository.bundles.external">
    <ivy pattern="http://repository.springsource.com/ivy/bundles/external/
                  [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
    <artifact pattern="http://repository.springsource.com/ivy/bundles/external/
                       [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
</url>
                

Then declare the following dependency:

<dependency org="org.springframework.javaconfig" name="org.springframework.config.java"
            rev="1.0.0.M4" conf="compile->runtime"/>
                

1.8. How to obtain nightly (aka 'snapshot') builds

1.8.1. Via manual download

Nightly builds are available from Spring's snapshot build area.

1.8.2. Via Maven

To access nightly builds using Maven, add the following repositories to your Maven pom:

<repository>
    <id>com.springsource.repository.bundles.snapshot</id>
    <name>SpringSource Enterprise Bundle Repository - SpringSource Snapshot Builds</name>
    <url>http://repository.springsource.com/maven/bundles/snapshot</url>
</repository>

<repository>
    <id>com.springsource.repository.bundles.external</id>
    <name>SpringSource Enterprise Bundle Repository - External Releases</name>
    <url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
                

Then add the following dependency:

<dependency>
    <groupId>org.springframework.javaconfig</groupId>
    <artifactId>org.springframework.config.java</artifactId>
    <version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
                

1.8.3. Via Ivy

To access nightly builds using Ivy, add the following repositories to your Ivy config:

<url name="com.springsource.repository.bundles.snapshot">
    <ivy pattern="http://repository.springsource.com/ivy/bundles/snapshot/
                  [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
    <artifact pattern="http://repository.springsource.com/ivy/bundles/snapshot/
                       [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
</url>

<url name="com.springsource.repository.bundles.external">
    <ivy pattern="http://repository.springsource.com/ivy/bundles/external/
                  [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
    <artifact pattern="http://repository.springsource.com/ivy/bundles/external/
                       [organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
</url>
                

Then declare the following dependency:

<dependency org="org.springframework.javaconfig" name="org.springframework.config.java"
            rev="1.0.0.BUILD-SNAPSHOT" conf="compile->runtime"/>
                

2. Authoring @Configuration classes

The central artifact in Spring JavaConfig is the @Configuration-annotated class. These classes consist principally of @Bean-annotated methods that define instantiation, configuration, and initialization logic for objects that will be managed by the Spring IoC container.

2.1. @Configuration

Annotating a class with the @Configuration indicates that the class may be used by JavaConfig as a source of bean definitions. The simplest possible @Configuration class would read as follows:

@Configuration
public class ApplicationConfig {
}
            

An application may make use of just one @Configuration-annotated class, or many. @Configuration can be considered the equivalent of XML's <beans/> element. Like <beans/>, it provides an opportunity to explicitly set defaults for all enclosed bean definitions.

@Configuration(defaultAutowire = Autowire.BY_TYPE, defaultLazy = Lazy.FALSE)
public class ApplicationConfig {
    // bean definitions follow
}
            

Because the semantics of the attributes to the @Configuration annotation are 1:1 with the attributes to the <beans/> element, this documentation defers to the beans-definition section of Chapter 3, IoC from the Core Spring documentation. See also the Javadoc for @Configuration for details on each of the available annotation attributes.

[Tip]Tip
Jump to Section 3.1, “ Bootstrapping applications with JavaConfigApplicationContext ” to see how @Configuration classes are used to create a Spring Application Context.

2.2. @Bean

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports most of the attributes offered by <bean/>, such as: init-method, destroy-method, autowiring, lazy-init, dependency-check, depends-on and scope.

2.2.1. Declaring a bean

To declare a bean, simply annotate a method with the @Bean annotation. When JavaConfig encounters such a method, it will execute that method and register the return value as a bean within a BeanFactory. By default, the bean name will be the same as the method name (see bean naming for details on how to customize this behavior). The following is a simple example of a @Bean method declaration:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}
                

For comparison sake, the configuration above is exactly equivalent to the following Spring XML:

<beans>
    <bean name="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
                

Both will result in a bean named transferService being available in the BeanFactory / ApplicationContext, bound to an object instance of type TransferServiceImpl:

transferService -> com.acme.TransferServiceImpl
                

2.2.2. Injecting dependencies

When @Beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        return new Foo(bar());
    }

    @Bean
    public Bar bar() {
        return new Bar();
    }
}
                

In the example above, the foo bean recevies a reference to bar via constructor injection.

2.2.3. Receiving lifecycle callbacks

2.2.3.1. Using JSR-250 annotations

JavaConfig, like the core Spring Framework, supports use of JSR-250 "Common Annotations". For example:

public class FooService {
    @PostConstruct
    public void init() {
        // custom initialization logic
    }
}

@Configuration
@AnnotationDrivenConfig
public class ApplicationConfig {
    @Bean
    public FooService fooService() {
        return new FooService();
    }
}
                    

In the above example, FooService declares @PostConstruct . By declaring JavaConfig's @AnnotationDrivenConfig on The @Configuration class, this annotation will be respected by the container and called immediately after construction. See The core framework documentation on support for JSR-250 annotations for further details.

2.2.3.2. Using Spring interfaces

Spring's lifecycle callbacks are fully supported. If a bean implements InitializingBean, DisposableBean, or Lifecycle, their respective methods will be called by the container in accordance with their Javadoc.

2.2.3.3.  Using @Bean initMethodName / destroyMethodName attributes

The @Bean annotation supports specifying arbitrary initialization and destruction callback methods, much like Spring XML's init-method and destroy-method attributes to the bean element:

public class Foo {
    public void init() {
        // initialization logic
    }
}
public class Bar {
    public void cleanup() {
        // destruction logic
    }
}
@Configuration
public class AppConfig {
    @Bean(initMethodName="init")
    public Foo foo() {
        return new Foo();
    }
    @Bean(destroyMethodName="cleanup")
    public Bar bar() {
        return new Bar();
    }
}
                    

Of course, in the case of Foo above, it would be equally as valid to call the init() method directly during construction:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...
}
                    

[Tip]Tip
Remember that because you are working directly in Java, you can do anything you like with your objects, and do not always need to rely on the container!

2.2.4. Using *Aware interfaces

The standard set of *Aware interfaces such as BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware, etc. are fully supported. Consider an example class that implements BeanFactoryAware:

public class AwareBean implements BeanFactoryAware {

    private BeanFactory factory;

    // BeanFactoryAware setter (called by Spring during bean instantiation)
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.factory = beanFactory;
    }

    public void close(){
        // do clean-up
    }
}
                

If the class above were declared as a bean as follows:

@Configuration
public class AppConfig {
    @Bean
    public AwareBean awareBean() {
        return new AwareBean();
    }
}
                

its setBeanFactory method will be called during initialization, providing the bean with access to its enclosing BeanFactory.

2.2.5. Specifying bean scope

2.2.5.1. Using @Bean's scope attribute

JavaConfig makes available each of the four standard scopes specified in Section 3.4, "Bean Scopes" of the Spring reference documentation.

The DefaultScopes class provides string constants for each of these four scopes. SINGLETON is the default, and can be overridden by supplying the scope attribute to @Bean annotation:

@Configuration
public class MyConfiguration {
    @Bean(scope=DefaultScopes.PROTOTYPE)
    public Encryptor encryptor() {
        // ...
    }
}
                    

2.2.5.2. @ScopedProxy

Spring offers a convenient way of working with scoped dependencies through scoped proxies. The easiest way to create such a proxy when using the XML configuration is the <aop:scoped-proxy/> element. JavaConfig offers equivalent support with the @ScopedProxy annotation, which provides the same semantics and configuration options.

If we were to port the the XML reference documentation scoped proxy example (see link above) to JavaConfig, it would look like the following:

// a HTTP Session-scoped bean exposed as a proxy
@Bean(scope = DefaultScopes.SESSION)
@ScopedProxy
public UserPreferences userPreferences() {
   return new UserPreferences();
}

@Bean
public Service userService() {
   UserService service = new SimpleUserService();
   // a reference to the proxied 'userPreferences' bean
   service.seUserPreferences(userPreferences());
   return service;
}
                    

2.2.5.3. Lookup method injection

As noted in the core documentation, lookup method injection is an advanced feature that should be comparatively rarely used. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. JavaConfig provides a natural means for implementing this pattern. Note that the example below is adapted from the example classes and configuration in the core documentation linked above.

package fiona.apple; 

public abstract class CommandManager { 
    public Object process(Object commandState) { 
        // grab a new instance of the appropriate Command interface 
        Command command = createCommand(); 
    
        // set the state on the (hopefully brand new) Command instance 
        command.setState(commandState); 
        return command.execute(); 
    } 
    
    // okay... but where is the implementation of this method? 
    protected abstract Command createCommand(); 
}
                    

JavaConfig can easily create a subclass of CommandManager where the abstract createCommand() is overridden in such a way that it 'looks up' a brand new (prototype) command object:

@Bean(scope=DefaultScopes.PROTOTYPE)
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command command() {
            return asyncCommand();
        }
    }
}
                    

2.2.6. Customizing bean naming

By default, JavaConfig uses a @Bean method's name as the name of the resulting bean. This functionality can be overridden, however, using the BeanNamingStrategy extension point.

public class Main {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext();
        ctx.setBeanNamingStrategy(new CustomBeanNamingStrategy());
        ctx.addConfigClass(MyConfig.class);
        ctx.refresh();
        ctx.getBean("customBeanName");
    }
}
                

[Note]Note

JavaConfigApplicationContext will be covered in detail in Chapter 3, Using @Configuration classes

For more details, see the API documentation for BeanNamingStrategy.

2.2.7. Working with Spring FactoryBean implementations

Spring provides many implementations of the FactoryBean interface. Usually these classes are used to support integrations with other frameworks. Take for example org.springframework.orm.hibernate3.LocalSessionFactoryBean. This class is used to create a Hibernate SessionFactory and requires as dependencies the location of Hibernate mapping files and a DataSource. Here's how it is commonly used in XML:

<beans>
    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
             <list>
                 <value>com/acme/Bank.hbm.xml</value>
                 <value>com/acme/Account.hbm.xml</value>
                 <value>com/acme/Customer.hbm.xml</value>
             </list>
        </property>
    </bean>

    <bean id="dataSource" class="...">
        <!-- ... -->
    </bean>
</beans>
                

The Spring container recognizes that LocalSessionFactoryBean implements the FactoryBean interface, and thus treats this bean specially: An instance of LocalSessionFactoryBean is instantiated, but instead of being directly returned, instead the getObject() method is invoked. It is the object returned from this call getObject() that is ultimately registered as the sessionFactory bean.

How then would we use LocalSessionFactoryBean in JavaConfig? The best approach is to extend the ConfigurationSupport base class and use the getObject() method:

@Configuration
public class DataAccessConfig extends ConfigurationSupport {
    @Bean
    public SessionFactory sessionFactory() {
        LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
        ArrayList<String> mappingFiles = new ArrayList<String>();
        mappingFiles.add("com/acme/Bank.hbm.xml");
        mappingFiles.add("com/acme/Account.hbm.xml");
        mappingFiles.add("com/acme/Customer.hbm.xml");
        factoryBean.setMappingResources(mappingFiles);
        return this.getObject(SessionFactory.class, factoryBean);
    }
    // ... other beans, including dataSource() ...
}

                

Notice the call to this.getObject(Class, FactoryBean)? This call ensures that any container callbacks are invoked on the FactoryBean object, and then returns the value from the FactoryBean's getObject() in a type-safe fashion.

3. Using @Configuration classes

3.1.  Bootstrapping applications with JavaConfigApplicationContext

JavaConfigApplicationContext provides direct access to the beans defined by @Configuration-annotated classes. For more information on the ApplicationContext API in general, please refer to the Core Spring documentation.

3.1.1. Construction Options

Instantiating the JavaConfigApplicationContext can be done by supplying @Configuration class literals to the constructor, and/or strings representing packages to scan for @Configuration classes.

3.1.1.1. Construction by class literal

Each of the class literals supplied to the constructor will be processed, and for each @Bean method encountered, JavaConfig will create a bean definition and ultimately instantiate and initialize the bean.

JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class);
Service service = context.getBean(Service.class);
                    

Passing multiple @Configuration classes:

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext(AppConfig.class, DataConfig.class);
Service service = context.getBean(Service.class);
                    

3.1.1.2. Construction by base package

Base packages will be scanned for the existence of any @Configuration classes. Any candidate classes will then be processed much as if they had been supplied directly as class literals to the constructor.

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext("com.acme.app.configuration");
Service service = context.getBean(Service.class);
                    

Passing multiple base packages:

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext("com.acme.configuration", "com.acme.other");
Service service = context.getBean(Service.class);
                    

Matching packages and classes by wildcard:

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext("**/configuration/**/*.class", "**/other/*Config.class");
Service service = context.getBean(Service.class);
                    

[Note]Note

The wildcard syntax for matching packages and classes above is based on Ant Patterns

3.1.1.3. Post-construction configuration

When one or more classes/packages are supplied as constructor arguments, a JavaConfigApplicationContext instance cannot be further configured. If post-construction configuration is preferred or required, use either the no-arg constructor, configure by calling setters, then manually refresh the context. After the call to refresh(), the context will be 'closed for configuration'.

JavaConfigApplicationContext context = new JavaConfigApplicationContext();
context.setParent(otherConfig);
context.setConfigClasses(AppConfig.class, DataConfig.class);
context.setBasePackages("com.acme.configuration");
context.refresh();
Service service = (Service) context.getBean("serviceA");
                    

[Note]Note

Whenever multiple packages and/or classes are used to instantiate a JavaConfigApplicationContext, order matters. This is important when considering what happens if two configuration classes define a bean with the same name. The last-specified class wins.

3.1.2. Accessing beans with getBean()

JavaConfigApplicationContext provides several variants of the getBean() method for accessing beans.

3.1.2.1. Type-safe access

The preferred method for accessing beans is with the type-safe getBean() method.

JavaConfigApplicationContext context = new JavaConfigApplicationContext(...);
Service service = context.getBean(Service.class);
                    

Disambuguation options

If more than one bean of type Service had been defined in the example above, the call to getBean() would have thrown an exception indicating an ambiguity that the container could not resolve. In these cases, the user has a number of options for disambiguation:

Indicating a @Bean as primary

Like Spring's XML configuration, JavaConfig allows for specifying a given @Bean as primary:

@Configuration
public class MyConfig {
    @Bean(primary=Primary.TRUE)
    public Service myService() {
        return new Service();
    }

    @Bean
    public Service backupService() {
        return new Service();
    }
}
                            

After this modification, all calls to getBean(Service.class) will return the primary bean

JavaConfigApplicationContext context = new JavaConfigApplicationContext(...);
Service service = context.getBean(Service.class); // returns the myService() primary bean
                            

Disambiguation by bean name

JavaConfig provides a getBean() variant that accepts both a class and a bean name for cases just such as these.

JavaConfigApplicationContext context = new JavaConfigApplicationContext(...);
Service service = context.getBean(Service.class, "myService");
                            

Because bean ids must be unique, this call guarantees that the ambiguity cannot occur.

Retrieve all beans of a given type

It is also reasonable to call the getBeansOfType() method in order to return all beans that implement a given interface:

JavaConfigApplicationContext context = new JavaConfigApplicationContext(...);
Map matchingBeans = context.getBeansOfType(Service.class);
                            

Note that this latter approach is actually a feature of the Core Spring Framework's AbstractApplicationContext (which JavaConfigApplicationContext extends) and is not type-safe, in that the returned Map is not parameterized.

3.1.2.2. String-based access

Beans may be accessed via the traditional string-based getBean() API as well. Of course this is not type-safe and requires casting, but avoids any potential ambiguity entirely:

JavaConfigApplicationContext context = new JavaConfigApplicationContext(...);
Service service = (Service) context.getBean("myService");
                    

3.2.  Bootstrapping web applications with JavaConfigWebApplicationContext

JavaConfigWebApplicationContext allows for seamlessly bootstrapping @Configuration classes within your servlet container's web.xml deployment descriptor. This process requires no Spring XML whatsoever:

<web-app>
    <!-- Configure ContextLoaderListener to use JavaConfigWebApplicationContext
         instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.config.java.context.JavaConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
         fully-qualified @Configuration classes -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>example.ApplicationConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use JavaConfigWebApplicationContext
             instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.config.java.context.JavaConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
             and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>example.web.ServletConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /main/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/main/*</url-pattern>
    </servlet-mapping>
</web-app>
            

For basic information regarding initialization parameters to DispatcherServlet and use of ContextLoaderListener, see Chapter 13. Web MVC framework of the Core Spring Framework documentation.

4. Modularizing configurations

While the simplest configuration may be expressed as a single class that exposes several beans, it is often desirable to modularize configurations for reuse and clarity.

4.1. Partitioning bean definitions into multiple @Configuration classes

The simplest technique for modularizing configurations is to split up a single @Configuration class into multiple smaller classes:

// monolithic configuration
@Configuration
public class AppConfig {
    @Bean
    public ServiceA serviceA() {
       // ...
    }

    @Bean
    public ServiceB serviceB() {
       // ...
    }

   // assume many bean definitions follow
}
            

The above configuration class might be supplied as a parameter to JavaConfigApplicationContext:

JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class);
ServiceA serviceA = context.getBean(ServiceA.class);
ServiceB serviceB = context.getBean(ServiceB.class);
            

We can easily partition this configuration such that bean definitions are spread across two classes, instead of one:

// partitioned configuration
@Configuration
public class AppConfigA {
    @Bean
    public ServiceA serviceA() {
       // ...
    }
}

@Configuration
public class AppConfigB {
    @Bean
    public ServiceB serviceB() {
       // ...
    }
}
            

Now simply supply both configuration classes to the constructor of JavaConfigApplicationContext:

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext(AppConfigA.class, AppConfigB.class);
// both beans are still available in the resulting application context
ServiceA serviceA = context.getBean(ServiceA.class);
ServiceB serviceB = context.getBean(ServiceB.class);
            

4.2. Referencing beans across @Configuration classes

One configuration class may need to reference a bean defined in another configuration class (or in XML, for that matter). The preferred mechanism for doing this is using Spring's @Autowired annotation:

4.2.1. Direct bean references with @Autowired

One @Configuration class may directly reference bean instances registered from another using Spring's @Autowired annotation.

@Configuration
public class ConfigOne {
    @Bean
    public AccountRepository accountRepository() {
        // create and return an AccountRepository object
    }
}

@Configuration
@AnnotationDrivenConfig
public class ConfigTwo {
    @Autowired AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}
                

Given that both these configuration classes are supplied to the application context at runtime, the AccountRepository bean declared in ConfigOne will be autowired (injected) into the AccountRepository field in ConfigTwo.

JavaConfigApplicationContext context =
    new JavaConfigApplicationContext(ConfigOne.class, ConfigTwo.class);
                

4.2.2. Fully-qualified bean references with @Autowired

In addition to being able to reference any particular bean definition as seen above, one @Configuration class may reference the instance of any other @Configuration class using @Autowired. This works because the @Configuration classes themselves are instantiated and managed as individual Spring beans.

@Configuration
public class ConfigOne {
    @Bean
    public AccountRepository accountRepository() {
        // create and return an AccountRepository object
    }
}

@Configuration
@AnnotationDrivenConfig
public class ConfigTwo {
    @Autowired ConfigOne configOne;

    @Bean
    public TransferService transferService() {
         // transferService references accountRepository in a 'fully-qualified' fashion:
        return new TransferServiceImpl(configOne.accountRepository());
    }
}
                

[Tip]Tip
The 'fully-qualified' approach is generally preferred as it provides a the significant advantage of being able to easily navigate within an IDE to the source of the referenced bean.

Open issue: Should @AnnotationDrivenConfig be enabled by default? Rationale: given that @Autowired is the preferred method for referencing external beans, it is likely to need to be enabled in all but the most trivial configurations. See SJC-219.

4.3.  Aggregating @Configuration classes with @Import

Thus far, we've seen how to break up bean definitions into multiple @Configuration classes and how to reference those beans across @Configuration boundaries. These scenarios have required providing all @Configuration classes to the constructor of a JavaConfigApplicationContext, and this is not always ideal. Often it is preferable to use an aggregation approach, where one @Configuration class logically imports the bean definitions defined by another.

The @Import annotation provides just this kind of support, and it is the direct equivalent of the <import/> element found in Spring beans XML files.

@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(...);
    }
}

@Configuration
@AnnotationDrivenConfig
@Import(DataSourceConfig.class) // <-- AppConfig imports DataSourceConfig
public class AppConfig extends ConfigurationSupport {
    @Autowired DataSourceConfig dataSourceConfig;

    @Bean
    public void TransferService transferService() {
        return new TransferServiceImpl(dataSourceConfig.dataSource());
    }
}
            

The bootstrapping of this application is simplified, as it only needs to supply AppConfig when instantiating a JavaConfigApplicationContext.

public class Main {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx =
            new JavaConfigApplicationContext(AppConfig.class); // specifies single class
        // ...
    }
}
            

Multiple configurations may be imported by supplying an array of classes to the @Import annotation

@Configuration
@Import({ DataSourceConfig.class, TransactionConfig.class })
public class AppConfig extends ConfigurationSupport {
    // @Bean methods here can reference @Bean methods in DataSourceConfig or TransactionConfig
}
            

4.4. ConfigurationSupport

As a convenience, @Configuration classses may extend ConfigurationSupport, primarily in order to facilitate easy lookup of beans from the enclosing ApplicationContext.

@Configuration
public class AppConfig extends ConfigurationSupport {
    @Bean
    public Service serviceA() {
         // referencing a bean of type DataSource declared elsewhere
        DataSource dataSource = this.getBean(DataSource.class);
        return new ServiceImpl(dataSource);
    }
}
        

5. Working with externalized values

5.1.  @ExternalValue

Externally defined values such as usernames, passwords, file paths, and the like may be accessed using @ExternalValue and one or more of JavaConfig's ValueSource annotations.

5.1.1. @ExternalValue fields

@Configuration
@PropertiesValueSource("classpath:com/acme/db.properties")
public class AppConfig {
    @ExternalValue("datasource.username") String username;

    @Bean
    public TestBean testBean() {
        return new TestBean(username);
    }
}
            

com/acme/db.properties will be read from the classpath and the value associated with key datasource.username will be injected into the username field. The contents of db.properties might be as follows:

datasource.username=scott
datasource.password=tiger
...
                

[Note]Note

An array of properties file locations may be supplied to @PropertiesValueSource, and along with classpath:, all of the standard Spring resource-loading prefixes are supported, such as file: and http:.

@Configuration
@PropertiesValueSource({"classpath:com/acme/a.properties", "file:/opt/acme/b.properties"})
public class AppConfig {
    // ...
}
                    

5.1.2. @ExternalValue methods

@ExternalValue may also be used as a method-level annotation

@Configuration
@PropertiesValueSource("classpath:com/acme/db.properties")
public abstract class AppConfig {
    @ExternalValue("datasource.username")
    abstract String username();

    @Bean
    public TestBean testBean() {
        return new TestBean(username());
    }
}
                

The primary advantage to using @ExternalValue methods is that rather than injecting the external value just once (as is done in the case of @ExternalValue fields), @ExternalValue methods are evaluated every time they're referenced. As this is not usually required, @ExternalValue fields are the preferred method. A downside of @ExternalValue methods is that they should be abstract, requiring you to declare the entire @Configuration class abstract, and this is not in alignment with the semantics users typically associate with using the abstract keyword.

5.2. Available ValueSource annotations

  • @PropertiesValueSource
  • @EnvironmentValueSource
  • @SystemPropertiesValueSource

ValueSource annotations may be used in conjunction:

@Configuration
@EnvironmentValueSource
@SystemPropertiesValueSource
@PropertiesValueSource("classpath:com/acme/db.properties")
public class AppConfig {
    @ExternalValue("datasource.username") String username;

    @Bean
    public TestBean testBean() {
        return new TestBean(username);
    }
}
            

In this example, datasource.username will be looked for in db.properties, in the set of environment variables present at runtime and in the system properties.

[Note]Note
Explicit ordering of ValueSource annotations is not yet supported but will be soon. See SJC-170 and SJC-171 for details.

6. Combining configuration styles

JavaConfig can be used in conjunction with any or all of Spring's other container configuration approaches.

6.1. JavaConfig and XML

6.1.1.  Bootstrapping JavaConfig from XML with ConfigurationPostProcessor

You may desire or be required to use XML as the primary mechanism for configuring the container, but wish to selectively use @Configuration classes to define certain beans. For such cases, JavaConfig provides ConfigurationPostProcessor, a Spring BeanPostProcessor capable of processing @Configuration classes.

<beans>
    <!-- first, define your individual @Configuration classes as beans -->
    <bean class="com.myapp.config.AppConfig"/>
    <bean class="com.myapp.config.DataConfig"/>

    <!-- be sure to include the JavaConfig bean post-processor -->
    <bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/>
</beans>
                

Then, bootstrap an XML ApplicationContext:

ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");
                

The beans defined in AppConfig and DataConfig will be available via context.

6.1.1.1. Configuring configurations

An added benefit that comes along with bootstrapping JavaConfig from XML is that the configuration bean instances are eligible, just as any other bean, for dependency injection:

<beans>
    <!-- a possible "configurable configuration" -->
    <bean class="org.my.company.config.AppConfiguration">
        <property name="env" value="TESTING"/>
        <property name="monitoring" value="true"/>
        <property name="certificates" value="classpath:/META-INF/config/MyCompany.certs"/>
    </bean>
    <!-- JavaConfig post-processor -->
    <bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/>
</beans>
                    

6.1.2.  Bootstrapping XML from JavaConfig with @ImportXml

The @ImportXml annotation is provided to support importing beans defined in XML into @Configuration classes.

datasource-config.xml:
<beans>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>
</beans>

            
@Configuration
@AnnotationDrivenConfig // enable the @Autowired annotation
@ImportXml("classpath:com/company/app/datasource-config.xml")
public class Config {
    // autowire the DataSource bean declared in datasource-config.xml
    @Autowired DataSource dataSource;

    @Bean
    public FooRepository fooRepository() {
        // inject the autowired-from-XML dataSource
        return new JdbcFooRepository(dataSource);
    }

    @Bean
    public FooService fooService() {
        return new FooServiceImpl(fooRepository());
    }
}
            
[Tip]Tip
Regardless of the bootstrapping mechanism used - ConfigurationPostProcessor or @ImportXml - bean references may always be bi-directional. XML-defined beans may reference @Configuration-defined beans and vice-versa.

6.2. JavaConfig and Annotation-Driven Configuration

6.2.1. @AnnotationDrivenConfig

Spring 2.5 introduced a new style of dependency injection with Annotation-Driven Injection. In Spring XML, Annotation-Driven Injection is enabled in the container by declaring

<context:annotation-config/>
                

In JavaConfig, this same functionality is enabled with the @AnnotationDrivenConfig annotation

@Configuration
@AnnotationDrivenConfig
public class Config {
    // may now use @Autowired to reference beans from other @Configuration classes, XML, etc
}
                

6.2.2. @ComponentScan

An equivalent for Spring XML's <context:component-scan/> is provided with the @ComponentScan annotation.

package com.company.foo;

@Service
public class FooServiceImpl implements FooService {
    private final FooRepository fooRepository;

    @Autowired
    public FooService(FooRepository fooRepository) {
        this.fooRepository = fooRepository;
    }

    // ...
}
                

package com.company.foo;

@Repository
public class JdbcFooRepository implements FooRepository {
    private final DataSource dataSource;

    @Autowired
    public FooRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // ...
}
                

@Configuration
@ComponentScan("com.company") // search the com.company package for @Component classes
@ImportXml("classpath:com/company/data-access-config.xml") // XML with DataSource bean
public class Config {
}
                

Because Spring's @Service and @Repository stereotype annotations are each meta-annotated with @Component, they are candidates for component scanning. Because FooServiceImpl and JdbcFooRepository both reside underneath the com.company package, they will be discovered during component scanning and will be autowired together. @ImportXml pulls in the DataSource bean, ensuring it will be available for autowiring into JdbcFooRepository.

With the above very minimal configuration in the Config class, we can bootstrap and use the application as follows:

public class Main {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(Config.class);
        FooService fooService = ctx.getBean(FooService.class);
        fooService.doStuff();
    }
}
                

Please see "Chapter 3, IoC" of the core spring documentation for additional detail on Annotation-Driven Injection support.

7. Transaction-management support

7.1. @AnnotationDrivenTx

JavaConfig provides full support for the annotation-driven declarative transaction management features provided by the core Spring Framework with the @AnnotationDrivenTx annotation:

public class FooServiceImpl implements FooService {
    @Transactional
    public void doStuff() {
        // invoke multiple calls to data access layer
    }
}

            

@Configuration
@AnnotationDrivenTx
public class Config {
    @Bean
    public FooService fooService() {
        return new FooServiceImpl(fooRepository());
    }

    @Bean
    public FooRepository fooRepository() {
        return new JdbcFooRepository(dataSource());
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManeger(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // create and return a new JDBC DataSource ...
    }
}

            

public class Main {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(Config.class);

        // The FooService retrieved from the container will be proxied for tx management
        FooService fooService = ctx.getBean(FooService.class);

        // call the @Transactional method on the proxy - transactional behavior is guaranteed
        fooService.doStuff();
    }
}

            

Like Spring XML's <tx:annotation-driven> element, @AnnotationDrivenTx expects the presence of a bean named transactionManager of type PlatformTransactionManager as in the example above. Should you wish to forego this convention and name a transaction manager bean another name, you may do as follows:

@Configuration
@AnnotationDrivenTx(transactionManager="txManager") // specify explicitly the bean to use
public class Config {
    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManeger(dataSource());
    }

    // other beans...
}

            

The other attributes available to the @AnnotationDrivenTx are similar to the attributes to the <tx:annotation-driven> element. See the related documentation and the JavaDoc for @AnnotationDrivenTx for details.

8. AOP support

JavaConfig focuses its AOP support on AspectJ 5 @Aspect-style aspects.

8.1. @AspectJAutoProxy

Fashioned after Spring XML's <aop:aspectj-autoproxy>, @AspectJAutoProxy detects any @Aspect beans and generates proxies as appropriate to weave the advice methods in those aspects against other beans in the container.

/**
 * An aspect that logs a message before any property (javabeans setter method) is invoked
 */
@Aspect
public class PropertyChangeTracker {
    private Logger logger = Logger.getLogger(PropertyChangeTracker.class);

    @Before("execution(void set*(*))")
    public void trackChange() {
        logger.info("property about to change");
    }
}
            

/**
 * A class with setter methods
 */
public class SimpleCache implements Cache {
    private int size = 100; // default value;
    private DataSource dataSource;

    public void setCacheSize(int size) {
        this.size = size;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}
            

/**
 * A @Configuration class that wires up TransferService and applies
 * the PropertyChangeTracker aspect.
 */
@Configuration
@AspectJAutoProxy
public class Config {

    // declare the aspect itself as a bean
    @Bean
    public PropertyChangeTracker propertyChangeTracker() {
        return new PropertyChangeTracker();
    }

    @Bean
    public Cache cache() {
        return new SimpleCache();
    }
}
            

/**
 * A main method to bootstrap the application
 */
public class Main {
    public static void main(String[] args) {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(Config.class);
        Cache cache = ctx.getBean(Cache.class);
        // should see "property about to change" message in the log when calling this line..
        cache.setCacheSize(1000);
    }
}
            

[Note]Note
The attributes to the @AspectJAutoProxy annotation are very similar to the attributes to the <aop:aspectj-autoproxy> element. See the related documentation and the JavaDoc for @AspectJAutoProxy for details.

8.1.1. @Aspect-annotated @Configuration classes

As a convenience, @Configuration classes may themselves be annotated with @Aspect, allowing for inline advice methods. Modifying the example above, the Config class could be rewritten as follows, and PropertyChangeTracker could be eliminated entirely:

@Aspect
@Configuration
@AspectJAutoProxy
public class Config {
    private Logger logger = Logger.getLogger(Config.class);

    // declare the advice method locally, eliminating the need for PropertyChangeTracker
    @Before("execution(void set*(*))")
    public void trackChange() {
        logger.info("property about to change");
    }

    @Bean
    public Cache cache() {
        return new SimpleCache();
    }
}
                

9. JMX support

9.1. @MBeanExport

As an equivalent to Spring XML's <context:mbean-export/> element, JavaConfig provides the @MBeanExport annotation.

/**
 * A performance monitor that implements a JMX MBean interface
 */
public class PerformanceMonitor implements PerformanceMonitorMBean {
    public int getHitCount() {
        return hitCount;
    }
    // ...
}

public interface PerformanceMonitorMBean {
    int getHitCount();
    // ...
}

            

/**
 * A @Configuration class that wires up a PerformanceMonitor bean and ensures
 * it is registered with the local MBean server using @MBeanExport
 */
@Configuration
@MBeanExport
public class Config {
    // declare the MBean class as a spring bean
    @Bean
    public PerformanceMonitor performanceMonitor() {
        return new PerformanceMonitor();
    }
}

            

[Note]Note
Like with <context:mbean-export/>, MBean classes can be defined in a number of ways - using traditional MBean interfaces as shown above, or with Spring's @ManagedResource and associated annotations, etc. See the core Spring Framework documentation on JMX support for details.

10. Testing support

10.1. JavaConfigContextLoader

The TestContext framework, first released with Spring 2.5 provides comprehensive support for system testing using the JUnit and TestNG testing frameworks. JavaConfig provides integration with the TestContext framework via JavaConfigContextLoader.

package com.bank;

/**
 * Configures the TransferService application
 */
@Configuration
public class TransferAppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository());
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // create and return a new DataSource
    }
}
            

/**
 * System tests for the TransferService application
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="com.bank.TransferAppConfig",
                      loader=JavaConfigContextLoader.class)
public class TransferServiceTests {
    // autowire in the beans to test from the TransferServiceApp application context
    @Autowired TransferService transferService;

    SimpleJdbcTemplate jdbcTemplate;

    // DataSource parameter gets autowired from the TransferServiceApp context
    @Autowired
    void initJdbcTemplate(DataSource dataSource) {
        this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
    }

    @Test
    public void testTransferFunds() {
        String balQuery = "select balance from accounts where id=?";

        // execute assertions against the jdbcTemplate that
        // prove initial conditions are correct
        assertThat(jdbcTemplate.queryForObject(balQuery, double.class, "2", equalTo(100.00d);

        // transfer 300 dollars from account with id 1 to account with id 2
        transferService.transfer(300.00d, "1", "2");

        // execute assertions against the jdbcTemplate that
        // the database has been updated properly.
        assertThat(jdbcTemplate.queryForObject(balQuery, double.class, "2", equalTo(400.00d);
    }

    // additional @Test methods...
}
            

[Note]Note
Unfortunately, due to compatibility constraints with the TestContext framework's @ContextConfiguration annotation, the fully-qualified classname for com.bank.TransferAppConfig must be expressed as a string. This has a negative effect on refactorability, and will be improved in the future if possible. Vote for SJC-238 if this improvement is important to you.

11. Extending JavaConfig

11.1. @Plugin

Similar to Spring 2.0's support for XML namespaces, JavaConfig provides an extensibility mechanism with the @Plugin annotation. The general intent is to allow for a more expressive and declarative way to register commonly used bean definitions.

To get a sense of what @Plugin can do, consider each of the following annotations already discussed in this document: @AnnotationDrivenConfig, @AnnotationDrivenTx, @MBeanExport, @PropertiesValueSource. These annotations are all "@Plugin annotations". Take AnnotationDrivenConfig for example:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Plugin(handler=AnnotationDrivenConfigHandler.class)
public @interface AnnotationDrivenConfig { }
            

@AnnotationDrivenConfig is 'meta-annotated' as @Plugin. Notice the handler attribute to @Plugin accepts AnnotationDrivenConfigHandler.class:

class AnnotationDrivenConfigHandler implements ConfigurationPlugin<AnnotationDrivenConfig> {

    public void handle(AnnotationDrivenConfig annotation, BeanDefinitionRegistry registry) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(registry);
    }

}
            

The handle method of any ConfigurationPlugin implementation is provided with the annotation (AnnotationDrivenConfig in this case), as well as a BeanDefinitionRegistry - this registry is the same registry that JavaConfig is using to register all objects created from @Bean methods. Therefore, ConfigurationPlugin implementations have direct control over the container and can do with it as they please. Any number and type of bean definitions may be registered, offloading tedious or repetitive work from the @Configuration class author.

[Note]Note
This documentation is preliminary and the @Plugin/ ConfigurationPlugin API may change before JavaConfig's GA release. To find out more about programming @Plugin annotations, study the existing set of annotations mentioned above, and don't hesitate to interact with us via the JavaConfig forum.