From 5730b8d316e796c0b4a20c7ae51ced84905ff157 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 12 Mar 2013 22:11:23 -0700 Subject: [PATCH] Document @Bean 'lite' mode vs @Configuration Rework the reference documentation to better distinguish the differences between @Bean methods used in @Comonent vs @Configuration classes. The 'Using the @Bean annotation' section now only covers concepts applicable when using @Bean methods in @Configuration _or_ @Component classes. Information only applicable to @Configuration classes has been moved to a new 'Using the @Configuration annotation' section. An additional sidebar section attempts to explain the differences between the two approaches. Issue: SPR-9425 --- src/reference/docbook/beans-java.xml | 807 ++++++++++++++------------- 1 file changed, 425 insertions(+), 382 deletions(-) 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> + Basic concepts: <literal>@Bean</literal> and <literal>@Configuration</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. + + 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. + - 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: + 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,6 +242,387 @@ public class AppConfig {
+
+ 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 + + 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 { + + @Bean + public TransferService transferService() { + return new TransferServiceImpl(); + } + +} + + 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 + +
+ +
+ Receiving lifecycle callbacks + + 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. + + 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; +} +
+
+ +
+ 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... + } + +} +
+
+ +
+ 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 inter-bean 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 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. + +
+ +
+ 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(); + + // 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(); +} + + 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(); + // 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 createCommand() { + return asyncCommand(); + } + } +} +
+ +
+ Further information about how Java-based configuration works + internally + + The following example shows a @Bean annotated + method being called twice: + + +@Configuration +public class AppConfig { + + @Bean + public ClientService clientService1() { + ClientServiceImpl clientService = new ClientServiceImpl(); + clientService.setClientDao(clientDao()); + return clientService; + } + @Bean + public ClientService clientService2() { + ClientServiceImpl clientService = new ClientServiceImpl(); + clientService.setClientDao(clientDao()); + return clientService; + } + + @Bean + public ClientDao clientDao() { + return new ClientDaoImpl(); + } +} + + clientDao() has been called once in + clientService1() and once in + clientService2(). Since this method creates a new + instance of ClientDaoImpl and returns it, you would + normally expect having 2 instances (one for each service). That definitely + would be problematic: in Spring, instantiated beans have a + singleton scope by default. This is where the magic + comes in: All @Configuration classes are subclassed at + startup-time with CGLIB. In the subclass, the child + method checks the container first for any cached (scoped) beans before it + calls the parent method and creates a new instance. Note that as of Spring + 3.2, it is no longer necessary to add CGLIB to your classpath because + CGLIB classes have been repackaged under org.springframework and included + directly within the spring-core JAR. + + The behavior could be different according to the scope of your + bean. We are talking about singletons here. + + + There are a few restrictions due to the fact that CGLIB dynamically + adds features at startup-time: + + Configuration classes should not be final + + + They should have a constructor with no arguments + + + +
+
+ + + +
Composing Java-based configurations @@ -547,366 +952,4 @@ jdbc.password=
- -
- 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 - - 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 { - - @Bean - public TransferService transferService() { - return new TransferServiceImpl(); - } - -} - - 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 - -
- -
- 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 receives a reference - to bar via constructor injection. -
- -
- Receiving lifecycle callbacks - - 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(); - - // 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(); -} - - 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(); - // 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 createCommand() { - return asyncCommand(); - } - } -} -
-
- -
- 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 - - The following example shows a @Bean annotated - method being called twice: - - -@Configuration -public class AppConfig { - - @Bean - public ClientService clientService1() { - ClientServiceImpl clientService = new ClientServiceImpl(); - clientService.setClientDao(clientDao()); - return clientService; - } - @Bean - public ClientService clientService2() { - ClientServiceImpl clientService = new ClientServiceImpl(); - clientService.setClientDao(clientDao()); - return clientService; - } - - @Bean - public ClientDao clientDao() { - return new ClientDaoImpl(); - } -} - - clientDao() has been called once in - clientService1() and once in - clientService2(). Since this method creates a new - instance of ClientDaoImpl and returns it, you would - normally expect having 2 instances (one for each service). That definitely - would be problematic: in Spring, instantiated beans have a - singleton scope by default. This is where the magic - comes in: All @Configuration classes are subclassed at - startup-time with CGLIB. In the subclass, the child - method checks the container first for any cached (scoped) beans before it - calls the parent method and creates a new instance. Note that as of Spring - 3.2, it is no longer necessary to add CGLIB to your classpath because - CGLIB classes have been repackaged under org.springframework and included - directly within the spring-core JAR. - - The behavior could be different according to the scope of your - bean. We are talking about singletons here. - - - There are a few restrictions due to the fact that CGLIB dynamically - adds features at startup-time: - - Configuration classes should not be final - - - They should have a constructor with no arguments - - - -