Bean factory post processors in Spring

on waitingforcode.com

Bean factory post processors in Spring

Spring allows us to make a lot of operations with beans. We can decide if they should be unique instance on container (singleton) or not (prototype). We can also make some of operations "in the fly", thanks to bean factory post processors.

In this short article we'll present the idea of bean factory post processors. In its first part, we'll discover what is hidden behind this concept. After we'll write some of illustration code to understand this idea better.

What are bean factory post processors in Spring ?

Sometimes we can need to implement some of dynamic behavior in our Spring applications. For example, imagine following scenario: in your site you want to display two texts, depending on day hour. On AM hours, you will display "Good morning". After, the displayed text will be "Good afternoon". In additionally, you have two daily deploys, on at 12 AM and the other at 12 PM. And don't forget that this text is must handled by one bean. We have now two choices: change application context file at every deploy or define a bean implementing org.springframework.beans.factory.config.BeanFactoryPostProcessor interface. The second solution is more elegant because we can write the code once and "forget" that it exists.

So, what is this elegant BeanFactoryPostProcessor ? It's an interface implemented by the beans which want to modify the definitions of other beans. Note that only the definitions can be modified, i.e. constructor arguments, property values. BeanFactoryPostProcessor beans are invoked before initialization of "normal" beans and it's the reason why only the meta data can be changed. The invocation is made through protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) of org.springframework.context.support.AbstractApplicationContext:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}

Inside PostProcessorRegistrationDelegate, the method in charge of bean factory post processors execution, is:

private static void invokeBeanFactoryPostProcessors(Collection postProcessors, ConfigurableListableBeanFactory beanFactory) {
  for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    postProcessor.postProcessBeanFactory(beanFactory);
  }
}

As you can guess after previous code, the main method to override by BeanFactoryPostProcessor implementations is postProcessBeanFactory. Is the place where the customization of beans definition can be made. The customization is made on org.springframework.beans.factory.config.BeanDefinition objects. As already written in the article about singleton and prototype beans in Spring Framework, they contain a lot of information about beans meta data: constructor arguments, property values or scope.

Sample of bean factory post processors in Spring

All important theoretical points were described in the previous part. In this part we'll focus on more pragmatic case. Do you remember the case of "Good morning" and "Good afternoon" from the first part ? If not, go back to it and come here after the reading. If yes, let's try to implement this case in the code. First, we'll define some beans inside configuration file:

 
	 

  

The first bean represents this one which will implement BeanFactoryPostProcessor interface. The second bean is the injected class which will display welcome text in the page. They are the codes of both beans:

// Welcomer.java
public class Welcomer {

  private String welcomeText;

  public void initWelcomer() {
    LOGGER.debug("Welcomer is initialized");
  }

  public void setWelcomeText(String welcomeText) {
    LOGGER.debug("Setting welcomeText to: "+welcomeText);
    this.welcomeText = welcomeText;
  }

  public String getWelcomeText() {
    return this.welcomeText;
  }

  @Override
  public String toString() {
    return "Welcomer {text: "+this.welcomeText+"}";
  }
	
}

// BeanModifier.java
public class BeanModifier implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
      Calendar calendar = Calendar.getInstance();
      if (calendar.get(Calendar.AM_PM) == Calendar.PM) {
        BeanDefinition welcomerDef = beanFactory.getBeanDefinition("welcomerBean");
        welcomerDef.getPropertyValues().add("welcomeText", "Good afternoon");
      }
    } catch (Exception e) {
        LOGGER.error("An error occurred on setting welcomeText", e);
    }
  }
}

// test method
ApplicationContext context = new FileSystemXmlApplicationContext("/home/bartosz/webapp/src/main/resources/META-INF/applicationContext.xml");

Welcomer welcomer = (Welcomer) context.getBean("welcomer");
System.out.println("Text: "+welcomer.getWelcomeText());

Your logger file should print:

Setting welcomeText to: Good afternoon
Welcomer is initialized
Text: Good afternoon

As you can see here, BeanModifier is called before the real initialization of Welcomer. Thanks to overridden postProcessBeanFactory method, we can check the day time programatically and set the right value for the property "welcomeText".

The article is short but it describes an utility that you can need in some "dynamic" scenarios. For example, you can have a game in your site where every application deploy will add some bonus points to the best users. Thanks to BeanFactoryPostProcessor beans, this treatment can be done automatically inside a Java method and not manually at every deploy.

Share on: