Java-based container configuration
- Basic concepts: @Configuration and
- @Bean
-
- The central artifact in Spring's new Java-configuration support is the
- @Configuration-annotated class. These
- classes consist principally of
- @Bean-annotated methods that define
- instantiation, configuration, and initialization logic for objects to be
- managed by the Spring IoC container.
-
- Annotating a class with the
- @Configuration indicates that the class can
- be used by the Spring IoC container as a source of bean definitions. The
- simplest possible @Configuration class
- would read as follows:
+ Basic concepts: @Bean and @Configuration
+
+
+ Full @Configuration vs 'lite' @Beans mode?
+ When @Bean methods are declared within
+ classes that are not annotated with
+ @Configuration they are referred to as being
+ processed in a 'lite' mode. For example, bean methods declared in a
+ @Component or even in a plain old
+ class will be considered 'lite'.
+ Unlike full @Configuration, lite
+ @Bean methods cannot easily declare inter-bean
+ dependencies. Usually one @Bean method should not
+ invoke another @Bean method when operating in
+ 'lite' mode.
+ Only using @Bean methods within
+ @Configuration classes is a recommended approach
+ of ensuring that 'full' mode is always used. This will prevent the same
+ @Bean method from accidentally being invoked
+ multiple times and helps to reduce subtle bugs that can be hard to track down
+ when operating in 'lite' mode.
+
+
+ The central artifacts in Spring's new Java-configuration support are
+ @Configuration-annotated classes and
+ @Bean-annotated methods.
+ The @Bean annotation is used to indicate that a
+ method instantiates, configures and initializes a new object to be managed by
+ the Spring IoC container. For those familiar with Spring's
+ <beans/> XML configuration the @Bean
+ annotation plays the same role as the <bean/>
+ element. You can use @Bean annotated methods with
+ any Spring @Component, however, they are most
+ often used with @Configuration beans.
+ Annotating a class with @Configuration
+ indicates that its primary purpose is as a source of bean definitions. Furthermore,
+ @Configuration classes allow inter-bean
+ dependencies to be defined by simply calling other @Bean
+ methods in the same class. The simplest possible
+ @Configuration class would read as follows:
@Configuration
public class AppConfig {
@Bean
@@ -33,16 +58,15 @@ public class AppConfig {
}
}
- For those more familiar with Spring <beans/>
- XML, the AppConfig class above would be equivalent to:
+ The AppConfig class above would be equivalent to the
+ following Spring <beans/> XML:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
- As you can see, the @Bean annotation plays the same
- role as the <bean/> element. The
- @Bean annotation will be discussed in depth in the
- sections below. First, however, we'll cover the various ways of creating a
- spring container using Java-based configuration.
+ The @Bean and @Configuration
+ annotations will be discussed in depth in the sections below. First, however, we'll
+ cover the various ways of creating a spring container using Java-based
+ configuration.
@@ -218,389 +242,245 @@ public class AppConfig {
-
- Composing Java-based configurations
-
-
- Using the @Import annotation
-
- Much as the <import/> element is used
- within Spring XML files to aid in modularizing configurations, the
- @Import annotation allows for loading
- @Bean definitions from another configuration
- class:@Configuration
-public class ConfigA {
- public @Bean A a() { return new A(); }
-}
-
-@Configuration
-@Import(ConfigA.class)
-public class ConfigB {
- public @Bean B b() { return new B(); }
-}
- Now, rather than needing to specify both
- ConfigA.class and ConfigB.class
- when instantiating the context, only ConfigB needs to
- be supplied
- explicitly:public static void main(String[] args) {
- ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
-
- // now both beans A and B will be available...
- A a = ctx.getBean(A.class);
- B b = ctx.getBean(B.class);
-}
- This approach simplifies container instantiation, as only one class
- needs to be dealt with, rather than requiring the developer to remember
- a potentially large number of @Configuration classes
- during construction.
+
+ Using the @Bean annotation
-
- Injecting dependencies on imported @Bean
- definitions
+ @Bean is a method-level annotation and
+ a direct analog of the XML <bean/> element. The
+ annotation supports some of the attributes offered by
+ <bean/>, such as: init-method, destroy-method, autowiring and
+ name.
- The example above works, but is simplistic. In most practical
- scenarios, beans will have dependencies on one another across
- configuration classes. When using XML, this is not an issue, per se,
- because there is no compiler involved, and one can simply declare
- ref="someBean" and trust that Spring will work it
- out during container initialization. Of course, when using
- @Configuration classes, the Java compiler places
- constraints on the configuration model, in that references to other
- beans must be valid Java syntax.
+ You can use the @Bean annotation in a
+ @Configuration-annotated or in a
+ @Component-annotated class.
- Fortunately, solving this problem is simple. Remember that
- @Configuration classes are ultimately just another
- bean in the container - this means that they can take advantage of
- @Autowired injection metadata just like any other
- bean!
+
+ Declaring a bean
- Let's consider a more real-world scenario with several
- @Configuration classes, each depending on beans
- declared in the
- others:@Configuration
-public class ServiceConfig {
- private @Autowired AccountRepository accountRepository;
+ To declare a bean, simply annotate a method with the
+ @Bean annotation. You use this method to
+ register a bean definition within an ApplicationContext of
+ the type specified as the method's return value. By default, the bean
+ name will be the same as the method name. The following is a simple
+ example of a @Bean method declaration:
+ @Configuration
+public class AppConfig {
- public @Bean TransferService transferService() {
- return new TransferServiceImpl(accountRepository);
+ @Bean
+ public TransferService transferService() {
+ return new TransferServiceImpl();
}
-}
-@Configuration
-public class RepositoryConfig {
- private @Autowired DataSource dataSource;
+}
- public @Bean AccountRepository accountRepository() {
- return new JdbcAccountRepository(dataSource);
- }
-}
+ The preceding configuration is exactly equivalent to the following
+ Spring XML:
+ <beans>
+ <bean id="transferService" class="com.acme.TransferServiceImpl"/>
+</beans>
-@Configuration
-@Import({ServiceConfig.class, RepositoryConfig.class})
-public class SystemTestConfig {
- public @Bean DataSource dataSource() { /* return new DataSource */ }
-}
+ Both declarations make a bean named transferService
+ available in the ApplicationContext, bound to an object
+ instance of type TransferServiceImpl:
+
+transferService -> com.acme.TransferServiceImpl
+
+
-public static void main(String[] args) {
- ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
- // everything wires up across configuration classes...
- TransferService transferService = ctx.getBean(TransferService.class);
- transferService.transfer(100.00, "A123", "C456");
-}
+
+ Receiving lifecycle callbacks
-
- Fully-qualifying imported beans for ease of navigation
+ Any classes defined with the
+ @Bean annotation support
+ the regular lifecycle callbacks and can use the
+ @PostConstruct and @PreDestroy
+ annotations from JSR-250, see JSR-250
+ annotations for further details.
- In the scenario above, using @Autowired works
- well and provides the desired modularity, but determining exactly
- where the autowired bean definitions are declared is still somewhat
- ambiguous. For example, as a developer looking at
- ServiceConfig, how do you know exactly where the
- @Autowired AccountRepository bean is declared?
- It's not explicit in the code, and this may be just fine. Remember
- that the SpringSource Tool Suite provides tooling that can render
- graphs showing how everything is wired up - that may be all you
- need. Also, your Java IDE can easily find all declarations and uses
- of the AccountRepository type, and will quickly
- show you the location of @Bean methods that
- return that type.
+ The regular Spring lifecycle callbacks are fully supported as well. If a bean
+ implements InitializingBean, DisposableBean,
+ or Lifecycle, their respective methods are called by the
+ container.
- In cases where this ambiguity is not acceptable and you wish to
- have direct navigation from within your IDE from one
- @Configuration class to another, consider
- autowiring the configuration classes themselves:
- @Configuration
-public class ServiceConfig {
- private @Autowired RepositoryConfig repositoryConfig;
+ The standard set of *Aware interfaces such as
+ BeanFactoryAware,
+ BeanNameAware,
+ MessageSourceAware, ApplicationContextAware, and
+ so on are also fully supported.
- public @Bean TransferService transferService() {
- // navigate 'through' the config class to the @Bean method!
- return new TransferServiceImpl(repositoryConfig.accountRepository());
+ The @Bean annotation supports
+ specifying arbitrary initialization and destruction callback methods,
+ much like Spring XML's init-method and
+ destroy-method attributes on the bean element:
+ public class Foo {
+ public void init() {
+ // initialization logic
}
-}
- In the situation above, it is completely explicit where
- AccountRepository is defined. However,
- ServiceConfig is now tightly coupled to
- RepositoryConfig; that's the tradeoff. This tight
- coupling can be somewhat mitigated by using interface-based or
- abstract class-based @Configuration classes.
- Consider the following:
- @Configuration
-public class ServiceConfig {
- private @Autowired RepositoryConfig repositoryConfig;
+}
- public @Bean TransferService transferService() {
- return new TransferServiceImpl(repositoryConfig.accountRepository());
+public class Bar {
+ public void cleanup() {
+ // destruction logic
}
}
@Configuration
-public interface RepositoryConfig {
- @Bean AccountRepository accountRepository();
+public class AppConfig {
+ @Bean(initMethod = "init")
+ public Foo foo() {
+ return new Foo();
+ }
+ @Bean(destroyMethod = "cleanup")
+ public Bar bar() {
+ return new Bar();
+ }
}
+
-@Configuration
-public class DefaultRepositoryConfig implements RepositoryConfig {
- public @Bean AccountRepository accountRepository() {
- return new JdbcAccountRepository(...);
+ 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;
}
-}
-@Configuration
-@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
-public class SystemTestConfig {
- public @Bean DataSource dataSource() { /* return DataSource */ }
-}
+ // ...
+}
-public static void main(String[] args) {
- ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
- TransferService transferService = ctx.getBean(TransferService.class);
- transferService.transfer(100.00, "A123", "C456");
-}
- Now ServiceConfig is loosely coupled with respect
- to the concrete DefaultRepositoryConfig, and
- built-in IDE tooling is still useful: it will be easy for the
- developer to get a type hierarchy of
- RepositoryConfig implementations. In this way,
- navigating @Configuration classes and their
- dependencies becomes no different than the usual process of
- navigating interface-based code.
-
-
+
+ When you work directly in Java, you can do anything you like with
+ your objects and do not always need to rely on the container
+ lifecycle!
+
-
- Combining Java and XML configuration
+
+ Specifying bean scope
- Spring's @Configuration class support does not
- aim to be a 100% complete replacement for Spring XML. Some facilities
- such as Spring XML namespaces remain an ideal way to configure the
- container. In cases where XML is convenient or necessary, you have a
- choice: either instantiate the container in an "XML-centric" way using,
- for example, ClassPathXmlApplicationContext, or in a
- "Java-centric" fashion using
- AnnotationConfigApplicationContext and the
- @ImportResource annotation to import XML as
- needed.
-
-
- XML-centric use of @Configuration
- classes
-
- It may be preferable to bootstrap the Spring container from XML
- and include @Configuration classes in an ad-hoc
- fashion. For example, in a large existing codebase that uses Spring
- XML, it will be easier to create @Configuration
- classes on an as-needed basis and include them from the existing XML
- files. Below you'll find the options for using
- @Configuration classes in this kind of
- "XML-centric" situation.
-
-
- Declaring @Configuration classes as plain
- Spring <bean/> elements
+
+ Using the @Scope
+ annotation
- Remember that @Configuration classes are
- ultimately just bean definitions in the container. In this example,
- we create a @Configuration class named
- AppConfig and include it within
- system-test-config.xml as a
- <bean/>definition. Because
- <context:annotation-config/> is switched
- on, the container will recognize the
- @Configuration annotation, and process the
- @Bean methods declared in
- AppConfig
- properly.@Configuration
-public class AppConfig {
- private @Autowired DataSource dataSource;
+
- public @Bean AccountRepository accountRepository() {
- return new JdbcAccountRepository(dataSource);
- }
+ You can specify that your beans defined with the
+ @Bean annotation should have a specific
+ scope. You can use any of the standard scopes specified in the Bean Scopes section.
- public @Bean TransferService transferService() {
- return new TransferService(accountRepository());
+ The default scope is singleton, but you can
+ override this with the @Scope
+ annotation:
+ @Configuration
+public class MyConfiguration {
+ @Bean
+ @Scope("prototype")
+ public Encryptor encryptor() {
+ // ...
}
-}
- system-test-config.xml
-<beans>
- <!-- enable processing of annotations such as @Autowired and @Configuration -->
- <context:annotation-config/>
- <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
-
- <bean class="com.acme.AppConfig"/>
-
- <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="url" value="${jdbc.url}"/>
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.password}"/>
- </bean>
-</beans>
- jdbc.properties
-jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
-jdbc.username=sa
-jdbc.password=
- public static void main(String[] args) {
- ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
- TransferService transferService = ctx.getBean(TransferService.class);
- // ...
}
+
-
- In system-test-config.xml above, the
- AppConfig<bean/> does not declare an
- id element. While it would be acceptable to do
- so, it is unnecessary given that no other bean will ever refer to
- it, and it is unlikely that it will be explicitly fetched from the
- container by name. Likewise with the DataSource
- bean - it is only ever autowired by type, so an explicit bean id
- is not strictly required.
-
-
+
+ @Scope and scoped-proxy
-
- Using <context:component-scan/> to
- pick up @Configuration classes
+ 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. Configuring your beans in Java with a @Scope annotation
+ offers equivalent support with the proxyMode attribute. The default is
+ no proxy (ScopedProxyMode.NO), but you can specify
+ ScopedProxyMode.TARGET_CLASS or
+ ScopedProxyMode.INTERFACES.
- Because @Configuration is meta-annotated with
- @Component,
- @Configuration-annotated classes are
- automatically candidates for component scanning. Using the same
- scenario as above, we can redefine
- system-test-config.xml to take advantage of
- component-scanning. Note that in this case, we don't need to
- explicitly declare
- <context:annotation-config/>, because
- <context:component-scan/> enables all the
- same
- functionality.system-test-config.xml
-<beans>
- <!-- picks up and registers AppConfig as a bean definition -->
- <context:component-scan base-package="com.acme"/>
- <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
+ If you port the scoped proxy example from the XML reference
+ documentation (see preceding link) to our
+ @Bean using Java, it would look like
+ the following:
+ // an HTTP Session-scoped bean exposed as a proxy
+@Bean
+@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
+public UserPreferences userPreferences() {
+ return new UserPreferences();
+}
- <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="url" value="${jdbc.url}"/>
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.password}"/>
- </bean>
-</beans>
-
+@Bean
+public Service userService() {
+ UserService service = new SimpleUserService();
+ // a reference to the proxied userPreferences bean
+ service.setUserPreferences(userPreferences());
+ return service;
+}
+
-
- @Configuration class-centric use of XML with
- @ImportResource
+
+ Customizing bean naming
- In applications where @Configuration classes
- are the primary mechanism for configuring the container, it will still
- likely be necessary to use at least some XML. In these scenarios,
- simply use @ImportResource and define only as much
- XML as is needed. Doing so achieves a "Java-centric" approach to
- configuring the container and keeps XML to a bare minimum.
- @Configuration
-@ImportResource("classpath:/com/acme/properties-config.xml")
+ By default, configuration classes use a
+ @Bean method's name as the name of the
+ resulting bean. This functionality can be overridden, however, with the
+ name attribute.
+ @Configuration
public class AppConfig {
- private @Value("${jdbc.url}") String url;
- private @Value("${jdbc.username}") String username;
- private @Value("${jdbc.password}") String password;
- public @Bean DataSource dataSource() {
- return new DriverManagerDataSource(url, username, password);
+ @Bean(name = "myFoo")
+ public Foo foo() {
+ return new Foo();
}
-}
- properties-config.xml
-<beans>
- <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
-</beans>
- jdbc.properties
-jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
-jdbc.username=sa
-jdbc.password=
- public static void main(String[] args) {
- ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
- TransferService transferService = ctx.getBean(TransferService.class);
- // ...
-}
-
-
-
-
-
- Using the @Bean annotation
-
- @Bean is a method-level annotation and
- a direct analog of the XML <bean/> element. The
- annotation supports some of the attributes offered by
- <bean/>, such as: init-method, destroy-method, autowiring and
- name.
- You can use the @Bean annotation in a
- @Configuration-annotated or in a
- @Component-annotated class.
+}
+
-
- Declaring a bean
+
+ Bean aliasing
- To declare a bean, simply annotate a method with the
- @Bean annotation. You use this method to
- register a bean definition within an ApplicationContext of
- the type specified as the method's return value. By default, the bean
- name will be the same as the method name. The following is a simple
- example of a @Bean method declaration:
+ As discussed in , it is sometimes
+ desirable to give a single bean multiple names, otherwise known as
+ bean aliasing. The name
+ attribute of the @Bean annotation accepts a String
+ array for this purpose.
@Configuration
public class AppConfig {
- @Bean
- public TransferService transferService() {
- return new TransferServiceImpl();
+ @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
+ public DataSource dataSource() {
+ // instantiate, configure and return DataSource bean...
}
-}
-
- The preceding configuration is exactly equivalent to the following
- Spring XML:
- <beans>
- <bean id="transferService" class="com.acme.TransferServiceImpl"/>
-</beans>
-
- Both declarations make a bean named transferService
- available in the ApplicationContext, bound to an object
- instance of type TransferServiceImpl:
-
-transferService -> com.acme.TransferServiceImpl
-
+}
+
+
+
+ Using the @Configuration annotation
+ @Configuration is a class-level annotation
+ indicating that an object is a source of bean definitions.
+ @Configuration classes declare beans via
+ public @Bean annotated methods. Calls to
+ @Bean methods on
+ @Configuration classes can also be used to
+ define inter-bean dependencies. See for
+ a general introduction.
- Injecting dependencies
+ Injecting inter-bean dependenciesWhen @Beans have dependencies on one
another, expressing that dependency is as simple as having one bean
@@ -622,157 +502,28 @@ public class AppConfig {
In the example above, the foo bean receives a reference
to bar via constructor injection.
+
+
+ This method of declaring inter-bean dependencies only works when
+ the @Bean method is declared within a
+ @Configuration class. You cannot declare
+ inter-bean dependencies using plain @Component
+ classes.
+
-
- Receiving lifecycle callbacks
+
+ Lookup method injection
- Beans declared in a
- @Configuration-annotated class support
- the regular lifecycle callbacks. Any classes defined with the
- @Bean annotation can use the
- @PostConstruct and @PreDestroy
- annotations from JSR-250, see JSR-250
- annotations for further details.
-
- The regular Spring lifecycle callbacks are fully supported as well. If a bean
- implements InitializingBean, DisposableBean,
- or Lifecycle, their respective methods are called by the
- container.
-
- The standard set of *Aware interfaces such as
- BeanFactoryAware,
- BeanNameAware,
- MessageSourceAware, ApplicationContextAware, and
- so on are also fully supported.
-
- The @Bean annotation supports
- specifying arbitrary initialization and destruction callback methods,
- much like Spring XML's init-method and
- destroy-method attributes on the bean element:
- public class Foo {
- public void init() {
- // initialization logic
- }
-}
-
-public class Bar {
- public void cleanup() {
- // destruction logic
- }
-}
-
-@Configuration
-public class AppConfig {
- @Bean(initMethod = "init")
- public Foo foo() {
- return new Foo();
- }
- @Bean(destroyMethod = "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;
- }
-
- // ...
-}
-
-
- When you work directly in Java, you can do anything you like with
- your objects and do not always need to rely on the container
- lifecycle!
-
-
-
-
- Specifying bean scope
-
-
- Using the @Scope
- annotation
-
-
-
- You can specify that your beans defined with the
- @Bean annotation should have a specific
- scope. You can use any of the standard scopes specified in the Bean Scopes section.
-
- The default scope is singleton, but you can
- override this with the @Scope
- annotation:
- @Configuration
-public class MyConfiguration {
- @Bean
- @Scope("prototype")
- public Encryptor encryptor() {
- // ...
- }
-}
-
-
-
- @Scope and scoped-proxy
-
- 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. Configuring your beans in Java with a @Scope annotation
- offers equivalent support with the proxyMode attribute. The default is
- no proxy (ScopedProxyMode.NO), but you can specify
- ScopedProxyMode.TARGET_CLASS or
- ScopedProxyMode.INTERFACES.
-
- If you port the scoped proxy example from the XML reference
- documentation (see preceding link) to our
- @Bean using Java, it would look like
- the following:
- // an HTTP Session-scoped bean exposed as a proxy
-@Bean
-@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
-public UserPreferences userPreferences() {
- return new UserPreferences();
-}
-
-@Bean
-public Service userService() {
- UserService service = new SimpleUserService();
- // a reference to the proxied userPreferences bean
- service.setUserPreferences(userPreferences());
- return service;
-}
-
-
-
- Lookup method injection
-
- As noted earlier, lookup method injection is an advanced feature that you should
- use rarely. It is useful in cases where a singleton-scoped bean has a
- dependency on a prototype-scoped bean. Using Java for this type of
- configuration provides a natural means for implementing this pattern.
- public abstract class CommandManager {
- public Object process(Object commandState) {
- // grab a new instance of the appropriate Command interface
- Command command = createCommand();
+ As noted earlier, lookup method injection is an advanced feature that you should
+ use rarely. It is useful in cases where a singleton-scoped bean has a
+ dependency on a prototype-scoped bean. Using Java for this type of
+ configuration provides a natural means for implementing this pattern.
+ 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);
@@ -783,11 +534,11 @@ public Service userService() {
protected abstract Command createCommand();
}
- Using Java-configuration support , you can create a subclass of
- CommandManager where the abstract
- createCommand() method is overridden in such a way that
- it looks up a new (prototype) command object:
- @Bean
+ Using Java-configuration support , you can create a subclass of
+ CommandManager where the abstract
+ createCommand() method is overridden in such a way that
+ it looks up a new (prototype) command object:
+ @Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
@@ -805,51 +556,9 @@ public CommandManager commandManager() {
}
}
}
-
-
-
-
- Customizing bean naming
-
- By default, configuration classes use a
- @Bean method's name as the name of the
- resulting bean. This functionality can be overridden, however, with the
- name attribute.
- @Configuration
-public class AppConfig {
-
- @Bean(name = "myFoo")
- public Foo foo() {
- return new Foo();
- }
-
-}
-
-
-
-
- Bean aliasing
-
- As discussed in , it is sometimes
- desirable to give a single bean multiple names, otherwise known as
- bean aliasing. The name
- attribute of the @Bean annotation accepts a String
- array for this purpose.
- @Configuration
-public class AppConfig {
-
- @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
- public DataSource dataSource() {
- // instantiate, configure and return DataSource bean...
- }
-
-}
-
-
-
-
+ Further information about how Java-based configuration works
internally
@@ -909,4 +618,338 @@ public class AppConfig {
+
+
+
+
+
+
+ Composing Java-based configurations
+
+
+ Using the @Import annotation
+
+ Much as the <import/> element is used
+ within Spring XML files to aid in modularizing configurations, the
+ @Import annotation allows for loading
+ @Bean definitions from another configuration
+ class:@Configuration
+public class ConfigA {
+ public @Bean A a() { return new A(); }
+}
+
+@Configuration
+@Import(ConfigA.class)
+public class ConfigB {
+ public @Bean B b() { return new B(); }
+}
+ Now, rather than needing to specify both
+ ConfigA.class and ConfigB.class
+ when instantiating the context, only ConfigB needs to
+ be supplied
+ explicitly:public static void main(String[] args) {
+ ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
+
+ // now both beans A and B will be available...
+ A a = ctx.getBean(A.class);
+ B b = ctx.getBean(B.class);
+}
+ This approach simplifies container instantiation, as only one class
+ needs to be dealt with, rather than requiring the developer to remember
+ a potentially large number of @Configuration classes
+ during construction.
+
+
+ Injecting dependencies on imported @Bean
+ definitions
+
+ The example above works, but is simplistic. In most practical
+ scenarios, beans will have dependencies on one another across
+ configuration classes. When using XML, this is not an issue, per se,
+ because there is no compiler involved, and one can simply declare
+ ref="someBean" and trust that Spring will work it
+ out during container initialization. Of course, when using
+ @Configuration classes, the Java compiler places
+ constraints on the configuration model, in that references to other
+ beans must be valid Java syntax.
+
+ Fortunately, solving this problem is simple. Remember that
+ @Configuration classes are ultimately just another
+ bean in the container - this means that they can take advantage of
+ @Autowired injection metadata just like any other
+ bean!
+
+ Let's consider a more real-world scenario with several
+ @Configuration classes, each depending on beans
+ declared in the
+ others:@Configuration
+public class ServiceConfig {
+ private @Autowired AccountRepository accountRepository;
+
+ public @Bean TransferService transferService() {
+ return new TransferServiceImpl(accountRepository);
+ }
+}
+
+@Configuration
+public class RepositoryConfig {
+ private @Autowired DataSource dataSource;
+
+ public @Bean AccountRepository accountRepository() {
+ return new JdbcAccountRepository(dataSource);
+ }
+}
+
+@Configuration
+@Import({ServiceConfig.class, RepositoryConfig.class})
+public class SystemTestConfig {
+ public @Bean DataSource dataSource() { /* return new DataSource */ }
+}
+
+public static void main(String[] args) {
+ ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
+ // everything wires up across configuration classes...
+ TransferService transferService = ctx.getBean(TransferService.class);
+ transferService.transfer(100.00, "A123", "C456");
+}
+
+
+ Fully-qualifying imported beans for ease of navigation
+
+ In the scenario above, using @Autowired works
+ well and provides the desired modularity, but determining exactly
+ where the autowired bean definitions are declared is still somewhat
+ ambiguous. For example, as a developer looking at
+ ServiceConfig, how do you know exactly where the
+ @Autowired AccountRepository bean is declared?
+ It's not explicit in the code, and this may be just fine. Remember
+ that the SpringSource Tool Suite provides tooling that can render
+ graphs showing how everything is wired up - that may be all you
+ need. Also, your Java IDE can easily find all declarations and uses
+ of the AccountRepository type, and will quickly
+ show you the location of @Bean methods that
+ return that type.
+
+ In cases where this ambiguity is not acceptable and you wish to
+ have direct navigation from within your IDE from one
+ @Configuration class to another, consider
+ autowiring the configuration classes themselves:
+ @Configuration
+public class ServiceConfig {
+ private @Autowired RepositoryConfig repositoryConfig;
+
+ public @Bean TransferService transferService() {
+ // navigate 'through' the config class to the @Bean method!
+ return new TransferServiceImpl(repositoryConfig.accountRepository());
+ }
+}
+ In the situation above, it is completely explicit where
+ AccountRepository is defined. However,
+ ServiceConfig is now tightly coupled to
+ RepositoryConfig; that's the tradeoff. This tight
+ coupling can be somewhat mitigated by using interface-based or
+ abstract class-based @Configuration classes.
+ Consider the following:
+ @Configuration
+public class ServiceConfig {
+ private @Autowired RepositoryConfig repositoryConfig;
+
+ public @Bean TransferService transferService() {
+ return new TransferServiceImpl(repositoryConfig.accountRepository());
+ }
+}
+
+@Configuration
+public interface RepositoryConfig {
+ @Bean AccountRepository accountRepository();
+}
+
+@Configuration
+public class DefaultRepositoryConfig implements RepositoryConfig {
+ public @Bean AccountRepository accountRepository() {
+ return new JdbcAccountRepository(...);
+ }
+}
+
+@Configuration
+@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
+public class SystemTestConfig {
+ public @Bean DataSource dataSource() { /* return DataSource */ }
+}
+
+public static void main(String[] args) {
+ ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
+ TransferService transferService = ctx.getBean(TransferService.class);
+ transferService.transfer(100.00, "A123", "C456");
+}
+ Now ServiceConfig is loosely coupled with respect
+ to the concrete DefaultRepositoryConfig, and
+ built-in IDE tooling is still useful: it will be easy for the
+ developer to get a type hierarchy of
+ RepositoryConfig implementations. In this way,
+ navigating @Configuration classes and their
+ dependencies becomes no different than the usual process of
+ navigating interface-based code.
+
+
+
+
+
+ Combining Java and XML configuration
+
+ Spring's @Configuration class support does not
+ aim to be a 100% complete replacement for Spring XML. Some facilities
+ such as Spring XML namespaces remain an ideal way to configure the
+ container. In cases where XML is convenient or necessary, you have a
+ choice: either instantiate the container in an "XML-centric" way using,
+ for example, ClassPathXmlApplicationContext, or in a
+ "Java-centric" fashion using
+ AnnotationConfigApplicationContext and the
+ @ImportResource annotation to import XML as
+ needed.
+
+
+ XML-centric use of @Configuration
+ classes
+
+ It may be preferable to bootstrap the Spring container from XML
+ and include @Configuration classes in an ad-hoc
+ fashion. For example, in a large existing codebase that uses Spring
+ XML, it will be easier to create @Configuration
+ classes on an as-needed basis and include them from the existing XML
+ files. Below you'll find the options for using
+ @Configuration classes in this kind of
+ "XML-centric" situation.
+
+
+ Declaring @Configuration classes as plain
+ Spring <bean/> elements
+
+ Remember that @Configuration classes are
+ ultimately just bean definitions in the container. In this example,
+ we create a @Configuration class named
+ AppConfig and include it within
+ system-test-config.xml as a
+ <bean/>definition. Because
+ <context:annotation-config/> is switched
+ on, the container will recognize the
+ @Configuration annotation, and process the
+ @Bean methods declared in
+ AppConfig
+ properly.@Configuration
+public class AppConfig {
+ private @Autowired DataSource dataSource;
+
+ public @Bean AccountRepository accountRepository() {
+ return new JdbcAccountRepository(dataSource);
+ }
+
+ public @Bean TransferService transferService() {
+ return new TransferService(accountRepository());
+ }
+}
+ system-test-config.xml
+<beans>
+ <!-- enable processing of annotations such as @Autowired and @Configuration -->
+ <context:annotation-config/>
+ <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
+
+ <bean class="com.acme.AppConfig"/>
+
+ <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+ <property name="url" value="${jdbc.url}"/>
+ <property name="username" value="${jdbc.username}"/>
+ <property name="password" value="${jdbc.password}"/>
+ </bean>
+</beans>
+ jdbc.properties
+jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
+jdbc.username=sa
+jdbc.password=
+ public static void main(String[] args) {
+ ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
+ TransferService transferService = ctx.getBean(TransferService.class);
+ // ...
+}
+
+
+ In system-test-config.xml above, the
+ AppConfig<bean/> does not declare an
+ id element. While it would be acceptable to do
+ so, it is unnecessary given that no other bean will ever refer to
+ it, and it is unlikely that it will be explicitly fetched from the
+ container by name. Likewise with the DataSource
+ bean - it is only ever autowired by type, so an explicit bean id
+ is not strictly required.
+
+
+
+
+ Using <context:component-scan/> to
+ pick up @Configuration classes
+
+ Because @Configuration is meta-annotated with
+ @Component,
+ @Configuration-annotated classes are
+ automatically candidates for component scanning. Using the same
+ scenario as above, we can redefine
+ system-test-config.xml to take advantage of
+ component-scanning. Note that in this case, we don't need to
+ explicitly declare
+ <context:annotation-config/>, because
+ <context:component-scan/> enables all the
+ same
+ functionality.system-test-config.xml
+<beans>
+ <!-- picks up and registers AppConfig as a bean definition -->
+ <context:component-scan base-package="com.acme"/>
+ <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
+
+ <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+ <property name="url" value="${jdbc.url}"/>
+ <property name="username" value="${jdbc.username}"/>
+ <property name="password" value="${jdbc.password}"/>
+ </bean>
+</beans>
+
+
+
+
+ @Configuration class-centric use of XML with
+ @ImportResource
+
+ In applications where @Configuration classes
+ are the primary mechanism for configuring the container, it will still
+ likely be necessary to use at least some XML. In these scenarios,
+ simply use @ImportResource and define only as much
+ XML as is needed. Doing so achieves a "Java-centric" approach to
+ configuring the container and keeps XML to a bare minimum.
+ @Configuration
+@ImportResource("classpath:/com/acme/properties-config.xml")
+public class AppConfig {
+ private @Value("${jdbc.url}") String url;
+ private @Value("${jdbc.username}") String username;
+ private @Value("${jdbc.password}") String password;
+
+ public @Bean DataSource dataSource() {
+ return new DriverManagerDataSource(url, username, password);
+ }
+}
+ properties-config.xml
+<beans>
+ <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
+</beans>
+ jdbc.properties
+jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
+jdbc.username=sa
+jdbc.password=
+ public static void main(String[] args) {
+ ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
+ TransferService transferService = ctx.getBean(TransferService.class);
+ // ...
+}
+
+
+