Spring @Configuration annotation

In this article, we will take a look at @Configuration annotation in spring framework and spring boot.
We will be covering what this annotation is, how to use it, and why it should be used.

@Configuration annotation
Spring @Configuration annotation is used to indicate that a class contains bean definitions.
These bean definitions can be used by the Spring container to create and configure beans.
It can be used on any class, but it is typically used on classes that are used to configure Spring beans.
This annotation can be used either with Java-based configuration or with XML-based configuration.

A class annotated with @Configuration contains one or more methods that return objects or beans. These methods are themselves annotated with @Bean.

Spring docs for @Configuration state,

Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.

A class with @Configuration contains methods that return spring beans. These methods are annotated with @Bean annotation.

A benefit of @Configuration is that classes with this annotation allow you to keep all of your bean definitions in a single place. This can be especially helpful if you have a large application with many different types of beans
Spring @Configuration example
Below is an example of Spring @Configuration annotation which contains a method to create an object of User.

@Configuration
public class AppConfiguration {
  @Bean
  public User getUser() {
    return new User();
  }
}

Below is the User class whose object is created in getUser() method

package com.codippa;

public class User {

  public User() {
    System.out.println("Creating user");
  }
}

To get a User object from Spring application context, use below code

AnnotationConfigApplicationContext c = 
             new AnnotationConfigApplicationContext();
c.register(AppConfiguration.class);
c.refresh();
User user = c.getBean(User.class);

When this code is executed, you will see below message printed on the console

Creating user

Methods annotated with @Bean are called immediately after refresh(), not when getBean() is called.

Notice that the class annotated with @Configuration is registered inside the application context with its register() method.
If you do not register it, then User will not be registered as a bean and Spring will not be able to find it, which is evident from the below error.

Exception in thread “main” org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘com.codippa.User’ available

Also, note that you should call refresh() on application context after registering Configuration class with it.
refresh() reloads the persistent configuration into Spring application context. If you do not call refresh(), then you get

Exception in thread “main” java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@42607a4f has not been refreshed yet

If your application is a web application, then you should use AnnotationConfigWebApplicationContext instead of AnnotationConfigApplicationContext.

Instead of writing above code to register @Configuration classes, you can also register them with XML configuration with following lines

<beans>
    <context:annotation-config/>
    <bean class="com.codippa.AppConfiguration"/>
 </beans>

Also, if you are using Spring boot, then there is no need to invoke register() in order to register @Configuration classes, since they are automatically registered by Spring at application startup.
That is, if you application starts like this

SpringApplication.run(DemoApplication.class, args);

then all the above steps are NOT required.

Lazy loading beans
All @Bean methods defined in @Configuration class are executed as the spring container starts. This means that @Bean  methods have eager initialization.
However, this behavior can be controlled with @Lazy annotation so that beans are initialized only when required. Example,

@Configuration
@Lazy
public class ConfigurationDemo {

  @Bean
  public BeanA beanA() {
    return new BeanA();
  }
}

@Bean method will not be called on application startup. It will be called when BeanA needs to be initialized.

@Lazy can also be used over individual @Bean methods as shown below

@Configuration
public class ConfigurationDemo {
  @Bean
  public BeanA beanA() {
    return new BeanA();
  }


  @Bean
  @Lazy
  public BeanC beanC() {
    return new BeanC();
  }
}

In this case, BeanA will be eagerly initialized while BeanC will be lazily initialized on demand.
@Configuration with @Profile
Similar to @Lazy, beans can be loaded on the basis of active profiles using @Profile annotation in conjunction with @Configuration.
@Profile can be applied at the class level so that all @Bean methods will be invoked if a particular profile is active as shown below.

@Configuration
@Profile("prod")
public class ConfigurationDemo {

  @Bean
  public DataSource getDataSource() {
    return new DataSource();
  }
}

In this case, getDataSource() will be invoked only when active profile is “prod”.

@Profile can be applied at individual @Bean methods so that they are invoked according to the active profile. Example,

@Configuration
public class ConfigurationDemo {
  @Bean
  @Profile("prod")
  public DataSource getProdDataSource() {
    return new ProdDataSource();
  }

  @Bean
  @Profile("staging")
  public DataSource getStagedDataSource() {
    return new StagedDataSource();
  }
}

In above example, @Bean method corresponding to the active profile will be invoked.

Needless to say, you can combine @Configuration@Lazy and @Profile annotations to load beans as they are required(lazily), according to the active profile.

Removing @Configuration
Even if you remove @Configuration from AppConfiguration class in above example, then also it will work. That is, you will get an instance of User from application context.

Then what is the use of @Configuration?

@Configuration ensures that the instances of objects created with @Bean are singleton. That is, even if getUser() is called multiple times, the object of User will be created only once.

But if @Configuration is removed, each call to getUser() will create a new object. Consider below example.

Create a new class UserDetail, which accepts an argument of type User

package com.codippa;

public class UserDetail {
  public UserDetail(User u) {
    System.out.println("Creating user detail");
  }
}

Now, add a new method to AppConfiguration that creates an instance of UserDetail.

// @Configuration
public class AppConfiguration {
  @Bean
  public User getUser() {
    return new User();
  }
  
  @Bean
  public UserDetail detail() {
    return new UserDetail(getUser());
  }
}

Notice that @Configuration annotation is commented. Now, when you run this code

AnnotationConfigApplicationContext c =
            new AnnotationConfigApplicationContext(); 
c.register(AppConfiguration.class); 
c.refresh();

Below logs are printed on the console

20:39:05.813 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory – Creating shared instance of singleton bean ‘getUser’
Creating user
20:39:05.861 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory – Creating shared instance of singleton bean ‘detail’
Creating user
Creating user detail

As you can see, User bean is created twice. One, when getUser() is called for @Bean and second, when it is called from detail() method.

That is all on @Configuration annotation in Spring.
Hope the article was informative.