Using the @Bean
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:
You can use the @Bean
annotation in a @Configuration
-annotated or in a
@Component
-annotated class.
Declaring a Bean
To declare a bean, you can 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 is the same as
the method name. The following example shows a @Bean
method declaration:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService() = 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
, as the
following text image shows:
transferService -> com.acme.TransferServiceImpl
You can also use default methods to define beans. This allows composition of bean configurations by implementing interfaces with bean definitions on default methods.
-
Java
public interface BaseConfig {
@Bean
default TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
public class AppConfig implements BaseConfig {
}
You can also declare your @Bean
method with an interface (or base class)
return type, as the following example shows:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService(): TransferService {
return TransferServiceImpl()
}
}
However, this limits the visibility for advance type prediction to the specified
interface type (TransferService
). Then, with the full type (TransferServiceImpl
)
known to the container only once the affected singleton bean has been instantiated.
Non-lazy singleton beans get instantiated according to their declaration order,
so you may see different type matching results depending on when another component
tries to match by a non-declared type (such as @Autowired TransferServiceImpl
,
which resolves only once the transferService
bean has been instantiated).
If you consistently refer to your types by a declared service interface, your
@Bean return types may safely join that design decision. However, for components
that implement several interfaces or for components potentially referred to by their
implementation type, it is safer to declare the most specific return type possible
(at least as specific as required by the injection points that refer to your bean).
|
Bean Dependencies
A @Bean
-annotated method can have an arbitrary number of parameters that describe the
dependencies required to build that bean. For instance, if our TransferService
requires an AccountRepository
, we can materialize that dependency with a method
parameter, as the following example shows:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
class AppConfig {
@Bean
fun transferService(accountRepository: AccountRepository): TransferService {
return TransferServiceImpl(accountRepository)
}
}
The resolution mechanism is pretty much identical to constructor-based dependency injection. See the relevant section for more details.
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, as the following example shows:
-
Java
-
Kotlin
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
class BeanOne {
fun init() {
// initialization logic
}
}
class BeanTwo {
fun cleanup() {
// destruction logic
}
}
@Configuration
class AppConfig {
@Bean(initMethod = "init")
fun beanOne() = BeanOne()
@Bean(destroyMethod = "cleanup")
fun beanTwo() = BeanTwo()
}
By default, beans defined with Java configuration that have a public You may want to do that by default for a resource that you acquire with JNDI, as its
lifecycle is managed outside the application. In particular, make sure to always do it
for a The following example shows how to prevent an automatic destruction callback for a
Also, with |
In the case of BeanOne
from the example above the preceding note, it would be
equally valid to call the init()
method directly during construction, as the
following example shows:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
@Configuration
class AppConfig {
@Bean
fun beanOne() = BeanOne().apply {
init()
}
// ...
}
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
Spring includes the @Scope
annotation so that you can specify the scope of a bean.
Using the @Scope
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,
as the following example shows:
-
Java
-
Kotlin
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
@Configuration
class MyConfiguration {
@Bean
@Scope("prototype")
fun encryptor(): Encryptor {
// ...
}
}
@Scope
and scoped-proxy
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 ScopedProxyMode.DEFAULT
, which
typically indicates that no scoped proxy should be created unless a different default
has been configured at the component-scan instruction level. You can specify
ScopedProxyMode.TARGET_CLASS
, ScopedProxyMode.INTERFACES
or ScopedProxyMode.NO
.
If you port the scoped proxy example from the XML reference documentation (see
scoped proxies) to our @Bean
using Java,
it resembles the following:
-
Java
-
Kotlin
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
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;
}
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
fun userPreferences() = UserPreferences()
@Bean
fun userService(): Service {
return SimpleUserService().apply {
// a reference to the proxied userPreferences bean
setUserPreferences(userPreferences())
}
}
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,
as the following example shows:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
@Configuration
class AppConfig {
@Bean("myThing")
fun thing() = Thing()
}
Bean Aliasing
As discussed in Naming Beans, 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.
The following example shows how to set a number of aliases for a bean:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
@Configuration
class AppConfig {
@Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource")
fun dataSource(): DataSource {
// instantiate, configure and return DataSource bean...
}
}
Bean Description
Sometimes, it is helpful to provide a more detailed textual description of a bean. This can be particularly useful when beans are exposed (perhaps through JMX) for monitoring purposes.
To add a description to a @Bean
, you can use the
@Description
annotation, as the following example shows:
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
@Configuration
class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
fun thing() = Thing()
}