Spring5 bean factory post processor

Spring allows us to use beans for a lot of operations (and this is the most common way of expressing our object-oriented thinking). We can decide whether they are the only instance (singleton or prototype) in the container. Through the bean factory post processor we can also 初始化perform some additional operations.

In this article, let's introduce the bean factory post processor . In the first part, we discover the mystery behind this concept. Later we will write some interesting code to let everyone understand the concept better.

What are post processors for Spring factories?

Sometimes we may need to implement some dynamic behavior in Spring application. As a simple example, suppose in your website, you want to display two text content by time. In the morning, you will display "Good Morning". In the afternoon, the text displayed will be "Good afternoon". Also, you have two daily deployments, one at 12am and another at 12pm. It should be emphasized that this text content must be handled by a bean. We now have two options: change the application context file each time we deploy (too cumbersome), or define a bean that implements the org.springframework.beans.factory.config.BeanFactoryPostProcessor interface. The second solution is more elegant, because we only need to write the code once, and then we can ignore its existence (no need to modify it again and again).

So, where is this graceful BeanFactoryPostProcessorbeauty? It is an interface implemented by beans that can modify the definitions of other beans. Note that only definitions can be modified, i.e. constructor parameters, property values. BeanFactoryPostProcessorThe bean is called before the "normal" bean is initialized, which is why it can modify the metadata (meta data). The call is implemented through the protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) of org.springframework.context.support.AbstractApplicationContext  :

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

Inside PostProcessorRegistrationDelegate, the method responsible for the execution of the bean factory post processor is:

/**
 * Invoke the given BeanFactoryPostProcessor beans.
 */
private static void invokeBeanFactoryPostProcessors(
		Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
	for (BeanFactoryPostProcessor postProcessor : postProcessors) {
		postProcessor.postProcessBeanFactory(beanFactory);
	}
}

As you can see, BeanFactoryPostProcessorthe main method is overridden by the implementation postProcessBeanFactory. This is also where we come to customize the bean definition ourselves. We do this by customizing on the org.springframework.beans.factory.config.BeanDefinition object. I've written about this in Spring 5 Source Parsing - Singletons and Prototype Beans in the Spring Framework, which ( BeanDefinition对象) contain a lot of information about bean metadata: constructor parameters, property values ​​or scope.

A simple Spring bean factory post processor Demo

Important parts of the theory have been described above. In this part, we focus on a simple and practical case. Do you remember the "Good morning" and "Good afternoon" examples from Part 1? If you forget, please go back and look again. Next, let's try to implement this case in code. First, we'll define some beans in the configuration file:

<bean class="com.migo.bean.BeanModifier"> 
      
<bean name="welcomerBean" class="com.migo.bean.Welcomer" init-method="initWelcomer">
  <property name="welcomeText" value="Good morning"></property>
 </bean>
</bean>

The first bean represents the bean that will implement BeanFactoryPostProcessorthe interface. The second bean is the injected class that displays the welcome text on the page. They are the code for two 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());

If it is afternoon, the output should be:

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

We can see that is called before the actual initialization of BeanModifier. WelcomerSince the overriding overrides the postProcessBeanFactorymethod, we can check the date and welcomeTextset the correct value for the property.

This article is short, but it describes some practical operations how we can implement it in a more efficient way in some "dynamic" scenarios. For example, you will encounter this. We often see that a game will have routine maintenance, then we will find that the leaderboard will be refreshed after the routine maintenance, and every time you log in to the game, some of your attributes or points will be updated. Refresh, in fact, every time you log in, you initialize your bean again, so we can do a lot of things, such as adding some bonus points for the best users. With BeanFactoryPostProcessorthis bean, this processing can be done automatically within the Java method, instead of having to do it manually every time we deploy.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325572475&siteId=291194637