BeanFactoryPostProcessor example

In this post under Spring Core, I will explain with example the purpose of “BeanFactoryPostProcessor” Spring class.

We define our Spring beans and give it to Spring container to create instances of those beans.

Spring container reads the bean definitions and create instances of beans.

What if we want to change the definition of those Spring beans at runtime.

That is where “BeanFactoryPostProcessor” Spring class comes into picture.

“BeanFactoryPostProcessor” comes after Spring reads the bean definitions and before creating any instances of those beans.

At this point we can change the bean definitions.

Lets see an example. For our example we create two calculator instances “GeneralCalculator” and “ScientificCalculator”.

GeneralCalculator

package core.package54;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component("generalCalculator")
@Primary
public class GeneralCalculator implements ICalculator {
    @Override
    public String toString() {
        return "GeneralCalculator";
    }
}

ScientificCalculator

package core.package54;

import org.springframework.stereotype.Component;

@Component("scientificCalculator")
public class ScientificCalculator implements ICalculator {
    @Override
    public String toString() {
        return "ScientificCalculator";
    }
}

The “GeneralCalculator” is marked with “@Primary” annotation.

Both implements “ICalculator” interface. Below is the structure of the interface.

ICalculator

package core.package54;

public interface ICalculator {
}

Next we will create “MathManager” class with below structure.

MathManager

package core.package54;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("mathManager")
public class MathManager {
    @Autowired
    private ICalculator iCalculator;

    public ICalculator getCalculator() {
        return iCalculator;
    }
}

Now when Spring creates an instance of “MathManager” it injects an instance of class that implements “ICalculator” interface.

In our case it is either “GeneralCalculator” or “ScientificCalculator” but since “GeneralCalculator” is marked with “@Primary” annotation, Spring prefers this always.

We can change this i.e., mark “ScientificCalculator” with “@Primary” annotation at runtime and remove “@Primary” annotation from “GeneralCalculator”.

So that when Spring plans to inject instances of class that implements “ICalculator” interface, it prefers “ScientificCalculator” instead of “GeneralCalculator”.

Below is the code that does the above.

MyBeanFactoryPostProcessor

package core.package54;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("scientificCalculator");
        beanDefinition.setPrimary(true);
        beanDefinition = beanFactory.getBeanDefinition("generalCalculator");
        beanDefinition.setPrimary(false);
    }
}

In the above code, at line 13, I get the bean definition of “ScientificCalculator” and set it as primary at line 14.

At line 15, I get the bean definition of “GenericCalculator” and set it as non-primary at line 16.

So as a result we have changed the bean definition of both “ScientificCalculator” and “GeneralCalculator” at runtime.

Below is the main class for your reference

Main class

package core.package54;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "core.package54")
public class Example54 {
    @Autowired
    private MathManager mathManager;

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Example54.class);
        MathManager mathManager = applicationContext.getBean(MathManager.class);
        ICalculator iCalculator = mathManager.getCalculator();
        System.out.println(iCalculator);
    }
}

Below is the output

Output

ScientificCalculator

In this way we can change the definition of beans at runtime.

Leave a comment