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 ApplicationContexts. 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!