Spring Boot - Register Beans Programmatically Examples

This tutorial shows you several ways to programmatically register beans inthe Spring framework.

In Spring, you can create objects that are managed by the Spring IoC container. Those objects are called beans. There are several ways to create a Spring Bean. The most common way is by adding a @Service, @Component, @Controller, or @Component annotation to the class. If you add the annotation, Spring will create an instance of the class and register it as a Spring Bean. Another way is by annotating a method with @Bean annotation. However, there are several cases that require manual or programmatic bean creation. For example, if you have to create multiple beans of a class with different names and property values. Spring provides many ways to register beans. Below are the examples.

For example, we have the following class and we want to create the beans of the class programmatically.

  public class MyComponent {
  
    private final String name;
  
    public MyComponent(String name) {
      this.name = name;
    }
  
    public String getName() {
      return this.name;
    }
  }

Using GenericWebApplicationContext

If you use Spring web, there should be a class named GenericWebApplicationContext The class has several methods for registering beans.

  public <T> void registerBean(
    Class<T> beanClass,
    Object... constructorArgs
  )

  public <T> void registerBean(
    @Nullable String beanName,
    Class<T> beanClass,
    Object... constructorArgs
  )

  public final <T> void registerBean(
    Class<T> beanClass,
    BeanDefinitionCustomizer... customizers
  )

  public final <T> void registerBean(
    @Nullable String beanName,
    Class<T> beanClass,
    BeanDefinitionCustomizer... customizers
  )

  public final <T> void registerBean(
    Class<T> beanClass,
    Supplier<T> supplier,
    BeanDefinitionCustomizer... customizers
  )

  public <T> void registerBean(
    @Nullable String beanName,
    Class<T> beanClass,
    @Nullable Supplier<T> supplier,
    BeanDefinitionCustomizer... customizers
  )

Basically, all the methods above require you to pass the class of the bean as the beanClass. Some of the methods which have a parameter named beanName allow you to set the bean name as well. If you use the method that doesn't have the beanName parameter, the class name will be used as the bean name.

There are three ways to define the object. For the methods with Object... constructorArgs parameter, you can pass some values to be passed as the arguments of the constructor. The methods with Supplier<T> supplier parameter require you to pass a function that returns the object. Meanwhile, the methods with BeanDefinitionCustomizer allow you to pass some functions for defining the bean. Below are the examples

  @Configuration
  public class FooConfig {
  
    public FooConfig(GenericWebApplicationContext context) {
      // 1
      context.registerBean(MyComponent.class, "foo1a");
  
      // 2
      context.registerBean("fooComponent1", MyComponent.class, "foo1b");
  
      // 3
      context.registerBean(MyComponent.class, () -> new MyComponent("foo2a"));
  
      // 4
      context.registerBean("fooComponent2", MyComponent.class, () -> new MyComponent("foo2b"));
  
      // 5
      context.registerBean(MyComponent.class, (BeanDefinition bd) -> {
        ConstructorArgumentValues constructorArgumentValues = bd.getConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, "foo3a");
      });
  
      // 6
      context.registerBean("fooComponent3", MyComponent.class, (BeanDefinition bd) -> {
        ConstructorArgumentValues constructorArgumentValues = bd.getConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, "foo3b");
      });
    }
  }

In the code above, the example number 1, 3 and 5 use the class name as the bean name. The example number 3 and 5 may cause an error because the bean with the same name is already registered. If you want to allow overriding, you can set the spring.main.allow-bean-definition-overriding property to true.

Since the code above uses @Configuration annotation, it will be run when the Spring application is started. If you want to register the beans later after the application has been started, you can also use GenericWebApplicationContext as it can be injected to the constructor of any Spring bean.

Using ConfigurableListableBeanFactory

ConfigurableListableBeanFactory is a configuration interface to be implemented by most listable bean factories. It has a method named registerSingleton, which can be used to register an object in the bean registry with a specified name.

  void registerSingleton(String beanName, Object singletonObject)

Using the method is quite simple. You need to pass the bean name as the first argument and the object as the second argument. Below is the usage example.

  @Configuration
  public class BarConfig {
  
    public BarConfig(ConfigurableListableBeanFactory beanFactory) {
      beanFactory.registerSingleton("barComponent", new MyComponent("bar"));
    }
  }

The ConfigurableListableBeanFactory can be injected to a Spring bean's constructor as well. Therefore, this can be used to perform bean registration later when the application has been started.

Using DefaultListableBeanFactory

DefaultListableBeanFactory is Spring's default implementation of the ConfigurableListableBeanFactory and @link BeanDefinitionRegistry interfaces. Below is the method of the class that you can use for bean registration. The method is defined in the BeanDefinitionRegistry interface which it implements.

  public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

The method allows you to define a bean using a BeanDefinition. A BeanDefinition is used to describe a bean instance which includes the constructor argument values, property values, and other information. We can create a BeanDefinition using a GenericBeanDefinition. Below is a basic example of using GenericBeanDefinition that sets the bean class and the argument values to be passed to the constructor. Another way to create a BeanDefinition is by using a BeanDefinitionBuilder

  @Configuration
  public class BazConfig {
  
    public BazConfig(DefaultListableBeanFactory beanFactory) {
      GenericBeanDefinition gbd = new GenericBeanDefinition();
      gbd.setBeanClass(MyComponent.class);
      gbd.getConstructorArgumentValues()
          .addIndexedArgumentValue(0, "baz1");
      beanFactory.registerBeanDefinition("bazComponent1", gbd);
  
      BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(MyComponent.class)
          .addConstructorArgValue("baz2");
      beanFactory.registerBeanDefinition("bazComponent2", bdb.getBeanDefinition());
    }
  }

The DefaultListableBeanFactory object can be injected to any Spring bean as well. Therefore, it's also possible to use it to register beans after the application has been started.

Using BeanFactoryPostProcessor Interface

BeanFactoryPostProcessor is a factory hook that allows for bean definitions modification. When the application is being started, Spring will scan the classes that implement this interface first before the configuration classes in the previous examples..

To implement the interface, the class needs to override the postProcessBeanFactory method. The method has a parameter whose type is ConfigurableListableBeanFactory which can be casted to DefaultListableBeanFactory. That means you can use the registerBeanDefinition method like the previous example. Below is the usage example.

  @Configuration
  public class CorgeConfig implements BeanFactoryPostProcessor {
  
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      GenericBeanDefinition gbd = new GenericBeanDefinition();
      gbd.setBeanClass(MyComponent.class);
      gbd.getConstructorArgumentValues().addIndexedArgumentValue(0, "qux");
  
      ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("quxComponent", gbd);
    }
  }

Using BeanDefinitionRegistryPostProcessor Interface

BeanDefinitionRegistryPostProcessor is an extension to the standard BeanFactoryPostProcessor. It can be used to register beans before BeanFactoryPostProcessor detection kicks in. In other words, Spring will scan classes that implement BeanFactoryPostProcessor first before classes that implement BeanFactoryPostProcessor.

A class that implements the interface also needs to override the postProcessBeanDefinitionRegistry method besides the postProcessBeanFactory method. The postProcessBeanDefinitionRegistry method has a parameter whose type is BeanDefinitionRegistry and it will be executed before the postProcessBeanFactory method. The BeanDefinitionRegistry interface has the following method.

  void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

Below is the usage example.

  @Configuration
  public class CorgeConfig implements BeanDefinitionRegistryPostProcessor {
  
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      GenericBeanDefinition gbd = new GenericBeanDefinition();
      gbd.setBeanClass(MyComponent.class);
      gbd.getConstructorArgumentValues().addIndexedArgumentValue(0, "corge");
      registry.registerBeanDefinition("corgeComponent", gbd);
    }
  
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      // Empty
    }
  }

Summary

In this tutorial, we have learned several ways to create and register beans programmatically in Spring. Creating a bean using one of the annotations mentioned earlier is the preferred way if possible. However, if you really need to dynamically register beans, you can choose any of the methods explained in this tutorial.

The code of this tutorial can be downloaded here.

You can also read about: