转载

Spring5源码解析-Spring中的bean工厂后置处理器

Spring允许我们使用bean来进行大量的操作(这也是我们面向对象思想最常用的表达方式)。我们可以决定它们在容器中是否是(单例或者是原型)唯一实例。通过bean工厂后置处理器我们还可以在 初始化 时进行一些额外的操作。

在这篇文章中,来介绍下 bean factory post processor 。在第一部分,我们来发现下这个概念背后玄机。之后我们会写一些有趣代码来让大伙更好地理解这个概念。

什么是Spring工厂的后置处理器?

有时我们可能需要在Spring应用程序中实现一些动态行为。举个简单的例子,假设在你的网站中,你要显示按时间来显示两个文本内容。上午的时候,你会显示“早安”。下午,显示的文字将是“下午好”。另外,你有两个日常部署,上午12点,另一个在晚上12点。需要强调的是,这个文本内容必须由一个bean来处理。我们现在有两个选择:每次部署时更改应用程序上下文文件(太麻烦了),或者定义一个实现 org.springframework.beans.factory.config.BeanFactoryPostProcessor 接口的bean 。第二个解决方案更优雅,因为我们只需要编写一次代码,然后就可以忽视它的存在了(无须次次去修改了)。

那么,这个优雅的 BeanFactoryPostProcessor 是何方神圣?它是由bean实现的接口,它们可以修改其他bean的定义。请注意,只能修改定义,即构造函数参数,属性值。 BeanFactoryPostProcessor bean在初始化“正常”bean之前被调用,这就是为什么它能修改元数据的原因(meta data)。调用是通过 org.springframework.context.support.AbstractApplicationContext的 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)实现的 :

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

PostProcessorRegistrationDelegate 里面,负责bean factory后置处理器执行的方法是:

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

如你所见,由 BeanFactoryPostProcessor 实现覆盖的主要方法是 postProcessBeanFactory 。这也是我们来自己定制bean定义的地方。我们通过在 org.springframework.beans.factory.config.BeanDefinition 对象上进行定制。关于这点我已经 在Spring5源码解析-Spring框架中的单例和原型bean 的文章中已经写过,它们( BeanDefinition对象 )包含大量关于bean元数据的信息:构造函数参数,属性值或作用域。

一个简单的Spring bean厂后置处理器Demo

关于理论的重要部分已经都在前面进行了描述。在这部分中,我们将重点放在一个简单实用的案例上。你还记得第一部分的“早安”和“下午好”的例子?如果忘了,请回去再瞅眼的。接着,让我们尝试在代码中实现这种案例。首先,我们将在配置文件中定义一些bean:

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

第一个bean代表将实现 BeanFactoryPostProcessor 接口的bean。第二个bean是注入的类,它会在页面中显示欢迎的文本内容。他们是两个bean的代码:

// 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;
  }
 
  publicStringgetWelcomeText(){
    return this.welcomeText;
  }
 
  @Override
  publicStringtoString(){
    return "Welcomer {text: "+this.welcomeText+"}";
  }
     
}
 
// BeanModifier.java
public class BeanModifierimplements BeanFactoryPostProcessor{
 
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throwsBeansException{
    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());

如果现在是下午,输出应该是:

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

我们可以看到, BeanModifierWelcomer 的真正初始化之前被调用。由于重写覆盖了 postProcessBeanFactory 方法,这样我们可以通过检查日期,并为属性 welcomeText 设置正确的值。

这篇文章虽短,但它描述了一些在一些“动态”场景中我们如何以一个更效率的方式来实现的实用操作。比如,你会碰到这种,我们常常见到一个游戏会有例行维护,那么我们会发现排行榜会在例行维护后刷新,你每次登录游戏也会对你的一些属性或者积分进行刷新,其实你每次登录就是又初始化了一遍你这个bean,这样,我们就可以做很多事情了,比如为最佳用户添加一些奖励积分。通过 BeanFactoryPostProcessor 这个 bean,这种处理就可以在Java方法内自动完成,无须我们在每次部署时通过手动来完成。

原文  https://muyinchen.github.io/2017/09/16/Spring5源码解析-Spring中的bean工厂后置处理器/
正文到此结束
Loading...