@Bean
is a method-level annotation and a direct analog of the XML
<bean/>
element. The annotation supports most of the attributes
offered by <bean/>
, such as:
init-method
,
destroy-method
,
autowiring
,
lazy-init
,
dependency-check
,
depends-on
and
scope
.
To declare a bean, simply annotate a method with the @Bean
annotation. When
JavaConfig encounters such a method, it will execute that method and register
the return value as a bean within a BeanFactory
. By default, the
bean name will be the same as the method name (see
bean naming for details on how to customize this behavior). The
following is a simple example of a @Bean
method declaration:
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
For comparison sake, the configuration above is exactly equivalent to the following Spring XML:
<beans> <bean name="transferService" class="com.acme.TransferServiceImpl"/> </beans>
Both will result in a bean named transferService
being available
in the BeanFactory
/ ApplicationContext
, bound to an
object instance of type TransferServiceImpl
:
transferService -> com.acme.TransferServiceImpl
When @Bean
s 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 recevies a reference to
bar
via constructor injection.
JavaConfig, like the core Spring Framework, supports use of JSR-250 "Common Annotations". For example:
public class FooService { @PostConstruct public void init() { // custom initialization logic } } @Configuration @AnnotationDrivenConfig public class ApplicationConfig { @Bean public FooService fooService() { return new FooService(); } }
In the above example, FooService
declares @PostConstruct
. By declaring JavaConfig's @AnnotationDrivenConfig
on The @Configuration
class, this annotation will be respected by the
container and called immediately after construction. See The core
framework documentation on support for JSR-250 annotations for further
details.
Spring's lifecycle
callbacks are fully supported. If a bean implements
InitializingBean
, DisposableBean
, or
Lifecycle
, their respective methods will be called by the
container in accordance with their Javadoc.
The @Bean
annotation supports specifying arbitrary initialization and
destruction callback methods, much like Spring XML's
init-method
and destroy-method
attributes to
the bean
element:
public class Foo { public void init() { // initialization logic } } public class Bar { public void cleanup() { // destruction logic } } @Configuration public class AppConfig { @Bean(initMethodName="init") public Foo foo() { return new Foo(); } @Bean(destroyMethodName="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; } // ... }
Tip | |
---|---|
Remember that because you are working directly in Java, you can do anything you like with your objects, and do not always need to rely on the container! |
The standard set of *Aware
interfaces such as
BeanFactoryAware
,
BeanNameAware
,
MessageSourceAware
,
ApplicationContextAware
,
etc. are fully supported. Consider an example class that implements
BeanFactoryAware
:
public class AwareBean implements BeanFactoryAware { private BeanFactory factory; // BeanFactoryAware setter (called by Spring during bean instantiation) public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.factory = beanFactory; } public void close(){ // do clean-up } }
If the class above were declared as a bean as follows:
@Configuration public class AppConfig { @Bean public AwareBean awareBean() { return new AwareBean(); } }
its setBeanFactory
method will be called during initialization,
providing the bean with access to its enclosing BeanFactory
.
JavaConfig makes available each of the four standard scopes specified in Section 3.4, "Bean Scopes" of the Spring reference documentation.
The DefaultScopes
class provides string constants
for each of these four scopes. SINGLETON is the default, and can be
overridden by supplying the scope
attribute to @Bean
annotation:
@Configuration public class MyConfiguration { @Bean(scope=DefaultScopes.PROTOTYPE) public Encryptor encryptor() { // ... } }
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.
JavaConfig offers equivalent support with the @ScopedProxy
annotation, which provides the same semantics and configuration options.
If we were to port the the XML reference documentation scoped proxy example (see link above) to JavaConfig, it would look like the following:
// a HTTP Session-scoped bean exposed as a proxy @Bean(scope = DefaultScopes.SESSION) @ScopedProxy public UserPreferences userPreferences() { return new UserPreferences(); } @Bean public Service userService() { UserService service = new SimpleUserService(); // a reference to the proxied 'userPreferences' bean service.seUserPreferences(userPreferences()); return service; }
As noted in the core documentation, lookup method injection is an advanced feature that should be comparatively rarely used. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. JavaConfig provides a natural means for implementing this pattern. Note that the example below is adapted from the example classes and configuration in the core documentation linked above.
package fiona.apple; 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(); }
JavaConfig can easily create a subclass of CommandManager
where the abstract createCommand()
is overridden in such
a way that it 'looks up' a brand new (prototype) command object:
@Bean(scope=DefaultScopes.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 command() { return asyncCommand(); } } }
By default, JavaConfig uses a @Bean
method's name as the name of the
resulting bean. This functionality can be overridden, however, using the
BeanNamingStrategy
extension point.
public class Main { public static void main(String[] args) { JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(); ctx.setBeanNamingStrategy(new CustomBeanNamingStrategy()); ctx.addConfigClass(MyConfig.class); ctx.refresh(); ctx.getBean("customBeanName"); } }
Note | |
---|---|
For more details, see the API documentation for
|
Spring provides many implementations of the FactoryBean
interface. Usually these classes are used to support integrations with other
frameworks. Take for example
org.springframework.orm.hibernate3.LocalSessionFactoryBean
. This
class is used to create a Hibernate SessionFactory and requires as dependencies
the location of Hibernate mapping files and a DataSource. Here's how it is
commonly used in XML:
<beans> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>com/acme/Bank.hbm.xml</value> <value>com/acme/Account.hbm.xml</value> <value>com/acme/Customer.hbm.xml</value> </list> </property> </bean> <bean id="dataSource" class="..."> <!-- ... --> </bean> </beans>
The Spring container recognizes that LocalSessionFactoryBean
implements the FactoryBean
interface, and thus treats this bean
specially: An instance of LocalSessionFactoryBean
is
instantiated, but instead of being directly returned, instead the
getObject()
method is invoked. It is the object returned from
this call getObject()
that is ultimately registered as the
sessionFactory
bean.
How then would we use LocalSessionFactoryBean
in JavaConfig?
The best approach is to extend the ConfigurationSupport
base
class and use the getObject()
method:
@Configuration public class DataAccessConfig extends ConfigurationSupport { @Bean public SessionFactory sessionFactory() { LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean(); factoryBean.setDataSource(dataSource()); ArrayList<String> mappingFiles = new ArrayList<String>(); mappingFiles.add("com/acme/Bank.hbm.xml"); mappingFiles.add("com/acme/Account.hbm.xml"); mappingFiles.add("com/acme/Customer.hbm.xml"); factoryBean.setMappingResources(mappingFiles); return this.getObject(SessionFactory.class, factoryBean); } // ... other beans, including dataSource() ... }
Notice the call to this.getObject(Class, FactoryBean)
? This call
ensures that any container callbacks are invoked on the
FactoryBean
object, and then returns the value from the
FactoryBean's getObject()
in a type-safe fashion.