Spring - Dynamically register beans

[Updated: Feb 25, 2017, Created: Dec 28, 2016]

If client code needs to register objects which are not managed by Spring container, then we will need to work with an instance of BeanDefinition.

A Spring application can register a BeanDefinition by using the following method of BeanDefinitionRegistry:

 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

BeanDefinition


org.springframework.core.AttributeAccessorAttributeAccessororg.springframework.beans.BeanMetadataElementBeanMetadataElementorg.springframework.beans.factory.config.BeanDefinitionBeanDefinitionLogicBig

BeanDefinition describes a bean instance. It has setter methods which can be used to programmatically set the Spring specific characteristics to a bean, for example BeanDefinition #setScope(String scope) can be used to set a scope other than default singleton.



GenericBeanDefinition


java.lang.ObjectObjectorg.springframework.core.AttributeAccessorSupportAttributeAccessorSupportorg.springframework.core.AttributeAccessorAttributeAccessorjava.io.SerializableSerializableorg.springframework.beans.BeanMetadataAttributeAccessorBeanMetadataAttributeAccessororg.springframework.beans.BeanMetadataElementBeanMetadataElementorg.springframework.beans.factory.support.AbstractBeanDefinitionAbstractBeanDefinitionorg.springframework.beans.factory.config.BeanDefinitionBeanDefinitionjava.lang.CloneableCloneableorg.springframework.beans.factory.support.GenericBeanDefinitionGenericBeanDefinitionLogicBig

This is the commonly used BeanDefinition implementation.

It allows to specify the bean class, bean characteristics plus constructor argument values and property values.




Using GenericBeanDefinition

public class MyBean {
  private Date date;

  public void doSomething () {
      System.out.println("from my bean, date: " + date);
  }

  public void setDate (Date date) {
      this.date = date;
  }
}
public class GenericBeanDefinitionExample {

  public static void main (String[] args) {
      DefaultListableBeanFactory context =
                new DefaultListableBeanFactory();

      GenericBeanDefinition gbd = new GenericBeanDefinition();
      gbd.setBeanClass(MyBean.class);

      MutablePropertyValues mpv = new MutablePropertyValues();
      mpv.add("date", new Date());

      //alternatively we can use:
      // gbd.getPropertyValues().addPropertyValue("date", new Date());
      gbd.setPropertyValues(mpv);

      context.registerBeanDefinition("myBeanName", gbd);

      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

Output

from my bean, date: Wed May 17 12:20:58 CDT 2017



Using BeanDefinitionBuilder

Only difference here is, BeanDefinitionBuilder uses builder pattern.

public class MyBean {
  private String str;

  public void setStr (String str) {
      this.str = str;
  }

  public void doSomething () {
      System.out.println("from MyBean " + str);
  }
}
public class BeanDefinitionBuilderExample {

  public static void main (String[] args) {
      DefaultListableBeanFactory beanFactory =
                new DefaultListableBeanFactory();

      BeanDefinitionBuilder b =
                BeanDefinitionBuilder.rootBeanDefinition(MyBean.class)
                                     .addPropertyValue("str", "myStringValue");

      beanFactory.registerBeanDefinition("myBean", b.getBeanDefinition());


      MyBean bean = beanFactory.getBean(MyBean.class);
      bean.doSomething();
  }
}

Output

from MyBean myStringValue



Injecting other bean references

public class MyBean {
  private MyOtherBean otherBean;

  public void setOtherBean (MyOtherBean otherBean) {
      this.otherBean = otherBean;
  }

  public void doSomething () {
      otherBean.doSomething();
  }
}
public class MyOtherBean {

  public void doSomething () {
      System.out.println("from other bean ");
  }
}
public class InjectingOtherBeans {

  public static void main (String[] args) {
      DefaultListableBeanFactory context =
                new DefaultListableBeanFactory();

      //define and register MyOtherBean
      GenericBeanDefinition beanOtherDef = new GenericBeanDefinition();
      beanOtherDef.setBeanClass(MyOtherBean.class);
      context.registerBeanDefinition("other", beanOtherDef);

      //definine and register myBean
      GenericBeanDefinition beanDef = new GenericBeanDefinition();
      beanDef.setBeanClass(MyBean.class);
      MutablePropertyValues mpv = new MutablePropertyValues();
      mpv.addPropertyValue("otherBean", context.getBean("other"));
      beanDef.setPropertyValues(mpv);
      context.registerBeanDefinition("myBean", beanDef);

      //using MyBean instance
      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

Output

from other bean 



Using BeanFactoryPostProcessor

BeanFactoryPostProcessor allows client code to customize bean definitions. The method BeanFactoryPostProcessor.postProcessBeanFactory is called by Spring startup process just after all bean definitions have been loaded, but no beans have been instantiated yet.

@Configuration
public class MyConfig {
  @Bean
  MyConfigBean myConfigBean () {
      return new MyConfigBean();
  }
}
public class MyConfigBean implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory (
            ConfigurableListableBeanFactory beanFactory)
            throws BeansException {

      GenericBeanDefinition bd = new GenericBeanDefinition();
      bd.setBeanClass(MyBean.class);
      bd.getPropertyValues().add("strProp", "my string property");

      ((DefaultListableBeanFactory) beanFactory)
                .registerBeanDefinition("myBeanName", bd);
  }
}
public class MyBean {
  private String strProp;

  public void setStrProp (String strProp) {
      this.strProp = strProp;
  }

  public void doSomething () {
      System.out.println("from MyBean:  " + strProp);
  }
}
public class BeanFactoryPostProcessorExample {

  public static void main (String[] args) {
      AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfig.class);
      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

Output

from MyBean:  my string property
WARNING: @Bean method MyConfig.myConfigBean is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.



Using BeanDefinitionRegistryPostProcessor

This is a sub-interface of BeanFactoryPostProcessor (last example). It allows for the registration of bean definitions. It's method postProcessBeanDefinitionRegistry is called before BeanFactoryPostProcessor#postProcessBeanFactory. This interface is more focused on the BeanDefinition registration rather than general purpose BeanFactoryPostProcessor.

@Configuration
public class MyConfig {
  @Bean
  MyConfigBean myConfigBean () {
      return new MyConfigBean();
  }
}
public class MyConfigBean
        implements BeanDefinitionRegistryPostProcessor {

  @Override
  public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry)
            throws BeansException {

      GenericBeanDefinition bd = new GenericBeanDefinition();
      bd.setBeanClass(MyBean.class);
      bd.getPropertyValues().add("strProp", "my string property");
      registry.registerBeanDefinition("myBeanName", bd);
  }

  @Override
  public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
      //no op
  }
}
public class MyBean {
  private String strProp;

  public void setStrProp (String strProp) {
      this.strProp = strProp;
  }

  public void doSomething () {
      System.out.println("from MyBean:  " + strProp);
  }
}
public class BeanDefinitionRegistryPostProcessorExample {

  public static void main (String[] args) {
      AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfig.class);

      MyBean bean = (MyBean) context.getBean("myBeanName");
      bean.doSomething();
  }
}

Output

from MyBean:  my string property
WARNING: Cannot enhance @Configuration bean definition 'beanDefinitionRegistryPostProcessorExample.MyConfig' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.



Example Project

Dependencies and Technologies Used :

  • Spring Context 4.3.4.RELEASE: Spring Context.
  • JDK 1.8
  • Maven 3.3.9

Dynamic Bean Registeration Select All Download
  • spring-bean-definition
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also