Pages

Monday, August 10, 2020

Spring Framework BeanDefinitionRegistryPostProcessor Example

1. Introduction


In this tutorial, You'll learn about BeanDefinitionRegistryPostProcessor interface in Spring and Spring Boot.

Spring Framework API - BeanDefinitionRegistryPostProcessor is used to register the bean dynamically with application context at runtime. This is most useful in the realtime when working with client libraries those are changed frequently. By using this bean, you can reduce the deployments of the application but you should use this with care. Otherwise, It will override the existing bean configuration.

Spring BeanDefinitionRegistryPostProcessor interface has one abstract method "void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException".

This is mainly useful if you have any third-party libraries which are not spring beans. So, these beans can be registered dynamically at runtime based on your need.

BeanDefinitionRegistryPostProcessor is also one of the interfaces to register the beans at runtime without using @Bean or @Component annotations in the program.

Spring Framework BeanDefinitionRegistryPostProcessor Example


From API:

Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.

Spring Boot BeanDefinitionRegistryPostProcessor


So, Any classes that implement the BeanDefinitionRegistryPostProcessor interface will be executed before the start BeanFactoryPostProcessor registration.


2. Spring BeanDefinitionRegistryPostProcessor Example


First, Created a bean class "MyNewBean" without any @Bean or @Componet annotation.

Second, I created a class with the name MyBeanRegistration that implements BeanDefinitionRegistryPostProcessor. And also you need to override the abstract method postProcessBeanDefinitionRegistry().

Register the bean with "GenericBeanDefinition" instance using setBeanClass(MyNewBean.class) method.

Third step is to register the MyBeanRegistration class with Spring boot. So, created another class MyCofig that is registered with Spring and registers the beans configured in it.

Finally, get the bean from the application context from the main method and call doSomeThing() method.


package com.javaprogramto.springboot.SpringBootCofigurations;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

class MyNewBean {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void doSomething() {
        System.out.println("MyNewBean name  " + name);
    }
}

class MyBeanRegistration
        implements BeanDefinitionRegistryPostProcessor {

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

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(MyNewBean.class);
        bd.getPropertyValues().add("name", "MycustomBean");

        registry.registerBeanDefinition("myNewBean", bd);
    }

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

    }
}

@Configuration
class MyConfig {
    @Bean
    MyBeanRegistration myConfigBean() {
       return new MyBeanRegistration();
   }
}

public class BeanDefinitionRegistryPostProcessorExample {

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

        MyNewBean bean = (MyNewBean) context.getBean("myNewBean");
        bean.doSomething();
    }

}


Output:


13:12:43.593 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'

13:12:43.594 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'

13:12:43.596 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'

13:12:43.597 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'

13:12:43.604 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myNewBean'
MyNewBean name  MycustomBean

3. BeanDefinitionRegistryPostProcessor Runtime Errors

Error 1:

com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample

12:37:06.914 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@9e89d68

12:37:06.935 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'

12:37:06.989 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfigBean'

12:37:06.992 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanDefinitionRegistryPostProcessorExample.MyConfig'

12:37:06.994 [main] INFO org.springframework.context.annotation.ConfigurationClassPostProcessor - 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'.

12:37:07.067 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'

12:37:07.068 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'

12:37:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'

12:37:07.071 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'

12:37:07.079 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myBeanName'

12:37:07.117 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBeanName': Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'strProp' of bean class [com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample$MyNewBean]: Bean property 'strProp' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBeanName': Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'strProp' of bean class [com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample$MyNewBean]: Bean property 'strProp' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1736)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1444)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)

 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)

 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)

 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)

 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)

 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)

 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)

 at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)

 at com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample.main(BeanDefinitionRegistryPostProcessorExample.java:15)

Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'strProp' of bean class [com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample$MyNewBean]: Bean property 'strProp' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

 at org.springframework.beans.BeanWrapperImpl.createNotWritablePropertyException(BeanWrapperImpl.java:243)

 at org.springframework.beans.AbstractNestablePropertyAccessor.processLocalProperty(AbstractNestablePropertyAccessor.java:426)

 at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:278)

 at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:266)

 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:97)

 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:77)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1732)

 ... 12 more

Error 2:



Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBeanName': Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'strProp' of bean class [com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample$MyNewBean]: Bean property 'strProp' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1736)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1444)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)

 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)

 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)

 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)

 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)

 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)

 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)

 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)

 at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)

 at com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample.main(BeanDefinitionRegistryPostProcessorExample.java:15)

Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'strProp' of bean class [com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample$MyNewBean]: Bean property 'strProp' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

 at org.springframework.beans.BeanWrapperImpl.createNotWritablePropertyException(BeanWrapperImpl.java:243)

 at org.springframework.beans.AbstractNestablePropertyAccessor.processLocalProperty(AbstractNestablePropertyAccessor.java:426)

 at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:278)

 at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:266)

 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:97)

 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:77)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1732)

 ... 12 more

Error 3:

Check the name of the bean that registered with BeanDefinitionRegistry..registerBeanDefinition("myNewBean"). 
You should use the same bean name when looking up in the ApplicationContext.

12:44:33.582 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myNewbean'
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myNewBean' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:808)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1279)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
at com.javaprogramto.springboot.SpringBootCofigurations.BeanDefinitionRegistryPostProcessorExample.main(BeanDefinitionRegistryPostProcessorExample.java:18)

4. Conclusion

In conclusion, You've learned how to register the beans with Spring at runtime using BeanDefinitionRegistryPostProcessor interface.


All the code is shown in this article is over GitHub.

You can download the project directly and can run in your local without any errors.



If you have any queries please post in the comment section.

No comments:

Post a Comment

Please do not add any spam links in the comments section.