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.

@Configuration
public class MyBeanConfig {
    @Bean
    public AntBeanFactoryPostProcessor antBeanFactoryPostProcessor() {
        return new AntBeanFactoryPostProcessor();
    }
}

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