How to Replace a Bean During Startup In Spring
There are circumstances in that we need to override a bean's methods which is from an external package but keep the same bean name. We can achieve this by replacing bean definition during application startup using BeanFactoryPostProcessor
.
BeanFactoryPostProcessor
A BeanFactoryPostProcessor
may interact with and modify bean definitions, but never bean instances. Allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory. Application contexts can auto-detect BeanFactoryPostProcessor beans in their bean definitions and apply them before any other beans get created.
Example
Following is the service from external packages, and we need to replace it:
@Service
public class Ant implements Animal{
private String animalName;
public Ant() {
this.animalName = "ant from original registration";
}
@Override
public void setAnimalName(String animalName) {
this.animalName = animalName;
}
@Override
public String getAnimalName() {
return animalName;
}
}
In order to replace it, we create AntBeanFactoryPostProcessor
that implements BeanFactoryPostProcessor
. We first remove the original bean definition, and register with new definition.
@Component
public class AntBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// remove original bean definition
((DefaultListableBeanFactory) configurableListableBeanFactory).removeBeanDefinition("ant");
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(Ant.class);
genericBeanDefinition.getPropertyValues().add("animalName", "ant from BeanFactoryPostProcessor");
((DefaultListableBeanFactory) configurableListableBeanFactory)
.registerBeanDefinition("ant", genericBeanDefinition);
}
}
Then we can check if the replacement is valid:
@SpringBootApplication
public class DependencyInjectionApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(DependencyInjectionApplication.class, args);
System.out.println("\n----- BeanFactoryPostProcessor register bean: ant");
Ant ant = applicationContext.getBean("ant", Ant.class);
System.out.println(ant.getAnimalName());
}
}
We would get the following result:
----- BeanFactoryPostProcessor register bean: ant
ant from BeanFactoryPostProcessor
Difference between BeanFactoryPostProcessor
and BeanDefinitionRegistryPostProcessor
BeanFactoryPostProcessor
and BeanDefinitionRegistryPostProcessor
both can be used to replace beans in Spring container, the difference is that BeanDefinitionRegistryPostProcessor
is executed earlier than BeanFactoryPostProcessor
. BeanDefinitionRegistryPostProcessor
is executed during Spring bootstrap, before the Spring container is fully initialized. BeanFactoryPostProcessor
is executed after the Spring container has been created but before any beans have been instantiated. So, BeanFactoryPostProcessor
is ideal for configuration changes to bean definitions during the container initialization phase while BeanDefinitionRegistryPostProcessor
allows one to modify the configuration metadata itself chatGPT.
Reading Recommendations
-
Four ways to dynamically register a bean
- GenericBeanDefinition
- BeanDefinitionBuilder
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor