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.
The simplest technique for modularizing configurations is to simply split up
large @Configuration
classes containing many
@Bean
definitions 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);
One configuration class may need to reference a bean defined in
another configuration class (or in XML, for that matter). The
@ExternalBean
annotation provides just such a
mechanism. When JavaConfig encounters a method annotated as
@ExternalBean
, it replaces that method
definition with a lookup to the enclosing bean factory for a bean with
the same name as the method name.
@Configuration
public class ConfigOne {
@Bean
public AccountRepository accountRepository() {
// create and return an AccountRepository
object
}
}
@Configuration
public abstract class ConfigTwo {
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository());
}
@ExternalBean
public abstract AccountRepository accountRepository();
}
Given that both these configuration classes are supplied to the
application context at runtime, JavaConfig will be able to resolve the
'accountRepository
'
@ExternalBean
by name and everything will
'wire-up' accordingly.
JavaConfigApplicationContext context = new JavaConfigApplicationContext(ConfigOne.class, ConfigTwo.class);
@Import
represents JavaConfig's
equivalent of XML configuration's <import/>
element.
One configuration class can import any number of other configuration classes,
and their bean definitions will be processed as if locally defined.
@Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { return new DriverManagerDataSource(...); } } @Configuration @Import(DataSourceConfig.class) public class AppConfig extends ConfigurationSupport { @Bean public void TransferService transferService() { return new TransferServiceImpl(getBean(DataSource.class); } }
Importing multiple configurations can be done by supplying an
array of classes to the @Import
annotation
@Configuration
@Import({ DataSourceConfig.class, TransactionConfig.class })
public class AppConfig extends ConfigurationSupport {
// bean definitions here can reference bean definitions in DataSourceConfig
or TransactionConfig
}
As a convenience, @Configuration
classses
can extend ConfigurationSupport
, primarily in
order to facilitate easy lookup of beans from the enclosing
BeanFactory
/
ApplicationContext
.
@Configuration public class AppConfig extends ConfigurationSupport { @Bean public Service serviceA() { DataSource dataSource = this.getBean(DataSource.class); // provided by base class // ... } }
Tip | |
---|---|
Important: This functionality may change! We are currently evaluating approaches for best supporting externalized values. See SJC-74 for more details. |
Access externally defined string values such as usernames,
passwords, and the like using @ResourceBundles
and @ExternalValue
.
@ResourceBundles("classpath:/org/springframework/config/java/simple") @Configuration public abstract static class ConfigWithPlaceholders { @ExternalValue("datasource.username") public abstract String username(); @Bean public TestBean testBean() { return new TestBean(username()); } }
The 'org/springframework/config/java/simple
'
argument to @ResourceBundles
tells JavaConfig
that there must be a file in the classpath at
org/springframework/config/java/simple[locale].[properties|xml]
from which key/value pairs can be read. For example:
datasource.username=scott datasource.password=tiger ...
Multiple values can be supplied to
@ResourceBundles
:
@ResourceBundles({"classpath:/com/foo/myapp/simple", "classpath:/com/foo/myapp/complex"})
@Configuration
public class AppConfig {
// ...
}
Consider the case of nested
@Configuration
classes:
@Configuration public class OuterConfig { @Bean public Foo outerBean() { // ... } @Configuration public static class InnerConfig { @Bean public Bar innerBean() { // ... } } }
Nested @Configuration
classes will be
processed as child ApplicationContext
s. Perhaps better said,
any declaring (outer) @Configuration
classes
will be processed as parent
ApplicationContexts
. Using the example
above, Instantiate an
ApplicationContext
using
InnerConfig
as an argument
JavaConfigApplicationContext context = new JavaConfigApplicationContext(InnerConfig.class);
context.getBean("innerBean"); // locally defined beans are available
context.getBean("outerBean"); // as are beans defined in the declaring OuterConfig
class.
Note that when supplying OuterConfig
as the argument, InnerConfig
is ignored
entirely. If it were to be processed, it would become a child context,
but its beans would would be inaccessible (parent contexts have no
access to child context beans).
JavaConfigApplicationContext context = new JavaConfigApplicationContext(OuterConfig.class);
context.getBean("outerBean"); // works fine
context.getBean("innerBean"); // throws NoSuchBeanDefinitionException
!