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