Close

Using Spring 5 BeanDefinitionCustomizer and new methods in GenericApplicationContext to programmatically register beans

[Updated: Nov 11, 2018, Created: Nov 11, 2018]

New methods in GenericApplicationContext

The existing class GenericApplicationContext is an implementation of ConfigurableApplicationContext interface. This class also implements BeanDefinitionRegistry interface. Typical usage of this class is to register a variety of bean definitions via the BeanDefinitionRegistry interface. In addition to the existing method registerBeanDefinition(beanName, beanDefinition), Spring 5.0 added following new convenient methods for registering beans:

package org.springframework.context.support;
 ......
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

   //existing implementation of BeanDefinitionRegistry#registerBeanDefinition method
   @Override
   public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 
                                                          throws BeanDefinitionStoreException {....}

   //spring 5.0 new methods
   //simple register of bean by its class 
   public final <T> void registerBean(Class<T> beanClass, BeanDefinitionCustomizer... customizers) {....}
   //simple register of bean by its name and class 
   public final <T> void registerBean(@Nullable String beanName, Class<T> beanClass
                                                               , BeanDefinitionCustomizer... customizers){....}
   //following two methods uses Java 8 Supplier which return the bean instance themselves, hence no reflection
   //involved in bean creation
   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) {....}
}  

Note that all new methods' last parameter is of new Spring 5.0 interface BeanDefinitionCustomizer.

GenericApplicationContext must call AbstractApplicationContext.refresh() to initialize the registered beans.

Spring 5.0 new interface BeanDefinitionCustomizer

package org.springframework.beans.factory.config;
 ....
@FunctionalInterface
public interface BeanDefinitionCustomizer {
   //Customize the given bean definition.
   void customize(BeanDefinition bd);
}

This interface can be used as a callback for customizing a given bean definition. It is designed for use with Java 8 lambda expressions or method references. Check out last tutorial on how to use BeanDefinition

Examples

Beans

public interface OrderService {
    void placeOrder(String item, int qty);

    public static class OrderServiceImpl implements OrderService {

        private LogService logService;

        public OrderServiceImpl() {
            System.out.printf("instance of %s created: %s%n", this.getClass().getName(),
                    System.identityHashCode(this));
        }

        public OrderServiceImpl(LogService logService) {
            this();
            this.logService = logService;
        }

        public void setLogService(LogService logService) {
            this.logService = logService;
        }

        @Override
        public void placeOrder(String item, int qty) {
            System.out.printf("placing order item: %s, qty: %s, isntance: %s%n",
                    item, qty, System.identityHashCode(this));
            if (logService != null) {
                logService.log("Order placed");
            }
        }

        private void init() {
            System.out.printf("%s, init method called: %s%n", this.getClass().getName(),
                    System.identityHashCode(this));
        }

        private void destroy() {
            System.out.printf("%s, destroy method called: %s%n", this.getClass().getName(),
                    System.identityHashCode(this));
        }
    }
}
public interface LogService {
    void log(String msg);

    class LogServiceImpl implements LogService {
        public LogServiceImpl() {
            System.out.printf("instance of %s created: %s%n", this.getClass().getName(),
                    System.identityHashCode(this));
        }

        @Override
        public void log(String msg) {
            System.out.println(msg);
        }

        private void init() {
            System.out.printf("%s, init method called: ", this.getClass().getName(),
                    System.identityHashCode(this));
        }
    }
}

Using new methods of BeanDefinitionCustomizer

Not using any customizer:

import org.springframework.context.support.GenericApplicationContext;

public class RegisterBeanExample1 {
  //using registerBean(beanClass, customizers)
  public static void main(String[] args) {
      GenericApplicationContext gac = new GenericApplicationContext();
      gac.registerBean(OrderService.OrderServiceImpl.class);//not using customizer
      gac.refresh();
      System.out.println("context refreshed");
      OrderService os = gac.getBean(OrderService.class);
      os.placeOrder("Laptop", 2);
      System.out.println("-----------");
      //retrieving the bean one more time
      os = gac.getBean(OrderService.class);
      os.placeOrder("Desktop", 2);
      gac.close();
  }
}
instance of OrderService$OrderServiceImpl created: 940553268
context refreshed
placing order item: Laptop, qty: 2, isntance: 940553268
-----------
placing order item: Desktop, qty: 2, isntance: 940553268

We have created a util class for BeanDefinitionCustomizers, so its methods can be used as Java 8 method references:

public class Customizers {

  public static void prototypeScoped(BeanDefinition bd) {
      bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE);
  }

  public static void lazy(BeanDefinition bd) {
      bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE);
  }


  public static void defaultInitMethod(BeanDefinition bd) {
      bd.setInitMethodName("init");
  }

  public static void defaultDestroyMethod(BeanDefinition bd) {
      bd.setDestroyMethodName("destroy");
  }
}

Registering bean as prototype scoped bean:

import org.springframework.context.support.GenericApplicationContext;

public class RegisterBeanExample2 {
  //using registerBean(beanClass, customizers)
  public static void main(String[] args) {
      GenericApplicationContext gac = new GenericApplicationContext();
      gac.registerBean(OrderService.OrderServiceImpl.class,
              Customizers::prototypeScoped);
      gac.refresh();
      System.out.println("context refreshed");
      OrderService os = gac.getBean(OrderService.class);
      os.placeOrder("Laptop", 2);
      System.out.println("-----------");
      //retrieving the bean one more time
      os = gac.getBean(OrderService.class);
      os.placeOrder("Desktop", 3);
      gac.close();
  }
}
context refreshed
instance of OrderService$OrderServiceImpl created: 1638172114
placing order item: Laptop, qty: 2, isntance: 1638172114
-----------
instance of OrderService$OrderServiceImpl created: 2034688500
placing order item: Desktop, qty: 3, isntance: 2034688500

Registering bean by name:

import org.springframework.context.support.GenericApplicationContext;

public class RegisterBeanExample3 {
  //using registerBean(beanName, beanClass, customizers)
  public static void main(String[] args) {
      GenericApplicationContext gac = new GenericApplicationContext();
      gac.registerBean("orderBean", OrderService.OrderServiceImpl.class,
              Customizers::lazy);
      gac.refresh();
      System.out.println("context refreshed");
      OrderService os = (OrderService) gac.getBean("orderBean");
      os.placeOrder("Laptop", 2);
      gac.close();
  }
}
context refreshed
instance of OrderService$OrderServiceImpl created: 110992469
placing order item: Laptop, qty: 2, isntance: 110992469

Using multiple BeanDefinitionCustomizers:

import org.springframework.context.support.GenericApplicationContext;

public class RegisterBeanExample4 {
  //using registerBean(beanClass, beanSupplier,  customizers)
  public static void main(String[] args) {
      GenericApplicationContext gac = new GenericApplicationContext();
      gac.registerBean(LogService.class, LogService.LogServiceImpl::new,
              Customizers::lazy, Customizers::defaultInitMethod);
      gac.refresh();
      System.out.println("context refreshed");
      LogService ls = gac.getBean(LogService.class);
      ls.log("msg from main method");
      gac.close();
  }
}
context refreshed
instance of LogService$LogServiceImpl created: 2114889273
LogService$LogServiceImpl, init method called: msg from main method

Injecting other bean via constructor:

import org.springframework.context.support.GenericApplicationContext;

public class RegisterBeanExample5 {
  //injecting other bean via constructor
  public static void main(String[] args) {
      GenericApplicationContext gac = new GenericApplicationContext();
      gac.registerBean(LogService.class, LogService.LogServiceImpl::new, Customizers::lazy,
              Customizers::defaultInitMethod);
      gac.registerBean(OrderService.OrderServiceImpl.class,
              Customizers::defaultInitMethod, Customizers::defaultDestroyMethod);
      gac.refresh();
      System.out.println("context refreshed");
      OrderService os = gac.getBean(OrderService.class);
      os.placeOrder("Laptop", 2);
      gac.close();
  }
}
instance of LogService$LogServiceImpl created: 586617651
LogService$LogServiceImpl, init method called: instance of OrderService$OrderServiceImpl created: 391447681
OrderService$OrderServiceImpl, init method called: 391447681
context refreshed
placing order item: Laptop, qty: 2, isntance: 391447681
Order placed
OrderService$OrderServiceImpl, destroy method called: 391447681

Example Project

Dependencies and Technologies Used:

  • spring-context 5.1.2.RELEASE: Spring Context.
  • JDK 1.8
  • Maven 3.5.4

Dynamic Bean Registration using Spring 5 methods Select All Download
  • spring-5-programmatic-bean-registration
    • src
      • main
        • java
          • RegisterBeanExample4.java

    See Also