diff --git a/src/reference/docbook/beans-java.xml b/src/reference/docbook/beans-java.xml index 8db15fb834..2a76971104 100644 --- a/src/reference/docbook/beans-java.xml +++ b/src/reference/docbook/beans-java.xml @@ -10,21 +10,46 @@ Java-based container configuration
- Basic concepts: <literal>@Configuration</literal> and - <literal>@Bean</literal> - - 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: <literal>@Bean</literal> and <literal>@Configuration</literal> + + + 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 <literal>@Import</literal> 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 <interfacename>@Bean</interfacename> annotation -
- Injecting dependencies on imported <literal>@Bean</literal> - 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 <literal>@Configuration</literal> - 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 <literal>@Configuration</literal> classes as plain - Spring <literal><bean/></literal> elements +
+ Using the <interfacename>@Scope</interfacename> + 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. - -
+
+ <code>@Scope and scoped-proxy</code> -
- Using <literal><context:component-scan/></literal> to - pick up <literal>@Configuration</literal> 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; +}
+
-
- <literal>@Configuration</literal> class-centric use of XML with - <literal>@ImportResource</literal> +
+ 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 <interfacename>@Bean</interfacename> 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 <interfacename>@Configuration</interfacename> 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 dependencies When @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 <interfacename>@Scope</interfacename> - 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() { - // ... - } -} -
- -
- <code>@Scope and scoped-proxy</code> - - 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 <literal>@Import</literal> 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 <literal>@Bean</literal> + 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 <literal>@Configuration</literal> + 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 <literal>@Configuration</literal> classes as plain + Spring <literal><bean/></literal> 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 <literal><context:component-scan/></literal> to + pick up <literal>@Configuration</literal> 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> +
+
+ +
+ <literal>@Configuration</literal> class-centric use of XML with + <literal>@ImportResource</literal> + + 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); + // ... +} +
+
+