DEV Community

irena-sayv
irena-sayv

Posted on

Spring Bean annotation: What, How, and Why?

@Bean is a method-level annotation that is used to create a spring managed bean. The object returned from the annotated method is registered as a bean in the Spring container. To simplify understanding, bean annotation is analogous to definition in XML configuration. In this blog, we will cover a basic understanding of @Bean with code examples.

How to create a bean using @Bean?

Bean annotated methods are generally placed in a @Configuration annotated class. During bootstrap, Spring reads the bean definition from the @Configuration class, and instantiates and initializes the bean. Bean definition methods can also be declared within a plain class or @Component annotated class but the processing of beans is different. We will touch base upon this concept a little later (See section — Bean Lite mode).

By default, the name of the method is the name of the created bean. You can give a custom name by using the name property of @Bean

public class EmployeeService {

public String getEmployeeName(String employeeCode) {
  // logic to return employee name based on employeeCode
 }
}
@Configuration
public class ApplicationConfig {

 @Bean(name = "customEmployeeService")
 public EmployeeService employeeServiceBean() {
  return new EmployeeService();
 }
}
Enter fullscreen mode Exit fullscreen mode

Here ApplicationConfig is a configuration class where we have provided definition for customEmployeeService bean

To use this bean, we can either

a) get the instance of the bean using the getBean method of ApplicationContext and use it in the consumer class

b) auto-wire it in the consumer class using @Autowire

Bean dependencies

Suppose EmployeeService has a dependency on EmployeeDao type. This is called inter-bean dependency which can be fulfilled through constructor injection or setter injection by directly invoking the bean method of the dependency in the dependent bean method.

Let's modify EmployeeService to include a constructor

public class EmployeeDao {

  public String getEmployeeName(String employeeCode) {
  // code to fetch employee name from databse and return from this method
 }
}

public class EmployeeService {

 private EmployeeDao employeeDao;

 public EmployeeService(EmployeeDao employeeDao) {
  this.employeeDao = employeeDao;
 }

 public String getEmployeeName(String employeeCode) {
  return employeeDao.getEmployeeName(employeeCode);
 }
}
Enter fullscreen mode Exit fullscreen mode

Now we define a bean of type EmployeeDao in ApplicationConfig and inject it in EmployeeService by calling the employeeDao() method in EmployeeService constructor

@Configuration
public class ApplicationConfig {

 @Bean
 public EmployeeDao employeeDao() {
  return new EmployeeDao();
 }

 @Bean(name = "customEmployeeBean")
 public EmployeeService employeeServiceBean() {
  return new EmployeeService(employeeDao());
 }
}
Enter fullscreen mode Exit fullscreen mode

Bean Lite mode

When a bean method is declared in a non @Configuration class — a @Component annotated class or even a plain class, the bean is said to be processed in ‘lite’ mode (‘full’ mode is when the method is declared within a @Configuration annotated class). The bean is still added to the context by component scan but the @Bean method simply acts as a factory method.

In the full mode example that we discussed in the Bean dependencies section, we declared inter-bean dependency in one @Bean method by calling another @Bean method. In lite mode, @Bean methods can not declare inter-dependencies. Invoking another @Bean method does not return the bean from the context, rather it is a normal java method invocation that returns a new instance.

The following code blocks illustrate the comparison between the full mode and lite mode of bean processing

@Configuration
public class ApplicationConfig {

 @Bean
 public EmployeeDao employeeDao() {
  System.out.println("Creating employeeDao bean");
  return new EmployeeDao();
 }

 @Bean(name = "customEmployeeBean")
 public EmployeeService employeeServiceBean() {
  return new EmployeeService(employeeDao());
 }
}


public class Application {

 public static void main(String[] args) {

  ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
  EmployeeService employeeService1 = context.getBean(EmployeeService.class);
  EmployeeService employeeService2 = context.getBean(EmployeeService.class);

 }
}

Output:
Creating employeeDao bean
Enter fullscreen mode Exit fullscreen mode
@Component
public class ApplicationConfig {

 @Bean
 public EmployeeDao employeeDao() {
  System.out.println("Creating employeeDao bean");
  return new EmployeeDao();
 }

 @Bean(name = "customEmployeeBean")
 public EmployeeService employeeServiceBean() {
  return new EmployeeService(employeeDao());
 }
}

public class Application {

 public static void main(String[] args) {

  ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
  EmployeeService employeeService1 = context.getBean(EmployeeService.class);
  EmployeeService employeeService2 = context.getBean(EmployeeService.class);

 }
}

Output:
Creating employeeDao bean
Creating employeeDao bean
Enter fullscreen mode Exit fullscreen mode

Why do we need @Bean when there is @Component?

In the above example, we created a @Configuration annotated class and manually defined our bean with @Bean. Why? We could have just annotated EmployeeService class with @Component (or @Service annotation to be more specific) for Spring to auto-detect and auto-configure it

@Bean can be used to create a bean of a class that is outside of the Spring container, which can be the case when the class is part of a library and you can not edit source code to add @Component annotation

@Bean also allows you to customize the instance before being used. For example, a RestTemplate configured with a custom timeout configuration to be used across the Spring boot application

@Configuration
public class ApplicationConfig {

 @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) 
    {
        return restTemplateBuilder
           .setConnectTimeout(Duration.ofSeconds(5))
           .setReadTimeout(Duration.ofSeconds(5))
           .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Hope this article helped to get a basic understanding of @Bean and how and when it can be used.

Top comments (0)