Spring进阶-BeanFactoryPostprocessor

什么是BeanFactoryPostProcessor

在前面的Spring进阶-BeanPostprocessor》中说过BeanPostprocessor主要是用在Bean的创建过程中对其进行扩展的,比如说@PostConstruct注解的实现原理就是通过BeanPostprocessor实现的。而这里要说的BeanFactoryPostProcessor,从名称上来说这两个名称比较相似。而实际上它们的主要作用也比较相似,都是用来为某些过程增加扩展的。不同的在于,BeanPostprocessor作用的目标为Bean,而BeanFactoryPostProcessor作用的对象为BeanFactory。通常我们可以用BeanFactoryPostProcessor来定制和修改BeanFactory的内容,例如覆盖或者添加属性。

接口定义

直接查看Spring源码可以知道,BeanFactoryPostProcessor只不过是一个接口,该接口的定义如下:

public interface BeanFactoryPostProcessor {
    
 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

接口定义很简单,只有一个方法postProcessBeanFactory。从源码中的注释我们可以知道,我们可以通过该方法在BeanFactory初始化之后修改程序上下文的内部BeanFactory,而此时的BeanDefinition都已经被加载了,但是Bean并未实例化。同时文档中有提及一点,在该过程中不要进行Bean实例相关交互,这可能导致实例提前创建发生异常情况。

如何使用

通过前面的内容你应该知道BeanFactoryPostProcessor是什么有什么用了,现在我们通过一个简单的例子来进一步了解。对于如何使用,我们完全可以对照BeanPostprocessor的来使用。

注册

关于如何注册主要有两种方式,最直白的方式就是通过ConfigurableApplicationContext提供的addBeanFactoryPostProcessor方法进行注册,另一种直接在容器中定义即可。

public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
  Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
  this.beanFactoryPostProcessors.add(postProcessor);
 }

上面就是AbstractApplicationContext中添加BeanFactoryPostProcessor的源代码,简单来说就是在内部使用List来存储注册的BeanFactoryPostProcessor。上面说过还有另外一种方式,该方式是直接在配置中定义一个BeanFactoryPostProcessor,容器会自动注册进来。这部分源码我们可以通过AbstractApplicationContext#refresh方法找到如何实现。该方法内部调用了如下代码:

invokeBeanFactoryPostProcessors(beanFactory);

该方法的内部主要实现则封装在该方法中:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

getBeanFactoryPostProcessors方法返回的就是beanFactoryPostProcessors这个List中的内容,而invokeBeanFactoryPostProcessors方法的内部主要实现如下:

public static void invokeBeanFactoryPostProcessors(
   ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors)
 
{
     //存储处理完的BeanFactoryPostProcessor
  Set<String> processedBeans = new HashSet<>();
     //处理BeanDefinitionRegistryPostProcessor(BeanFactoryPostProcessor子接口)
  if (beanFactory instanceof BeanDefinitionRegistry) {
   BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            //存储常规的BeanFactoryPostProcessor
   List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            //存储BeanDefinitionRegistryPostProcessor
   List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
   //遍历入参传入的BeanFactoryPostProcessor并添加到regularPostProcessors或者registryProcessors中
   for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                //执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法
    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
     BeanDefinitionRegistryPostProcessor registryProcessor =
       (BeanDefinitionRegistryPostProcessor) postProcessor;
     registryProcessor.postProcessBeanDefinitionRegistry(registry);
     registryProcessors.add(registryProcessor);
    }
    else {
     regularPostProcessors.add(postProcessor);
    }
   }
            //临时变量
   List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

   // 处理实现了PriorityOrdered的BeanDefinitionRegistryPostProcessors
   String[] postProcessorNames =
     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.classtruefalse);
   for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
     currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
     processedBeans.add(ppName);
    }
   }
            //排序并添加到列表
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   registryProcessors.addAll(currentRegistryProcessors);
            //执行postProcessBeanDefinitionRegistry方法
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            //清除临时变量
   currentRegistryProcessors.clear();

   // 处理实现了Ordered的BeanDefinitionRegistryPostProcessors
   postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.classtruefalse);
   for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
     currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
     processedBeans.add(ppName);
    }
   }
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   registryProcessors.addAll(currentRegistryProcessors);
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   currentRegistryProcessors.clear();

   // 处理剩下的BeanDefinitionRegistryPostProcessors直到不再出现
   boolean reiterate = true;
   while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.classtruefalse);
    for (String ppName : postProcessorNames) {
     if (!processedBeans.contains(ppName)) {
      currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
      processedBeans.add(ppName);
      reiterate = true;
     }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
   }

   // 执行BeanFactoryPostProcessor#方法postProcessBeanFactory
   invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
   invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  }

  else {
   // Invoke factory processors registered with the context instance.
   invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
  }

  // 获取所有的BeanFactoryPostProcessor
  String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.classtruefalse);
  //存储实现了PriorityOrdered接口的BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
     //存储实现了Ordered接口的BeanFactoryPostProcessor
  List<String> orderedPostProcessorNames = new ArrayList<>();
     //存储没有实现任何排序相关接口的BeanFactoryPostProcessor
  List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  for (String ppName : postProcessorNames) {
   if (processedBeans.contains(ppName)) {
    // 跳过之前处理过的
   }
   else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
   }
   else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName);
   }
   else {
    nonOrderedPostProcessorNames.add(ppName);
   }
  }

  // 排序并执行实现了PriorityOrdered接口的BeanFactoryPostProcessor
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // 排序并执行实现了Ordered接口的BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  for (String postProcessorName : orderedPostProcessorNames) {
   orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  sortPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

  // 执行剩下的BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  for (String postProcessorName : nonOrderedPostProcessorNames) {
   nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
  beanFactory.clearMetadataCache();
 }

从上面源码我们可以看出,我们即使不调用手动addBeanFactoryPostProcessor方法注册BeanFactoryPostProcessor也是可以应用的。从上面的源码中我们可以了解到,BeanFactoryPostProcessor执行是有顺序的。

实现BeanFactoryPostProcessor接口

通过前面我们知道了如何把BeanFactoryPostProcessor注册到容器中了,接下来就是如何使用了。使用BeanFactoryPostProcessor时我们需要特别注意的一点是,与我们交互的是BeanDefinition而不是Bean实例。例如现在我们有个需求是将BeanDefinition中的属性值由小写转成大写,这时候我们就可以使用BeanFactoryPostProcessor来完成。

public class App1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bfp.xml");

        System.out.println("打印BeanDefinition");
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            System.out.println(propertyValue.getName() + " => " + propertyValue.getValue());
        }

        System.out.println("打印实例");
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

class MyBeanFactoryPostprocessor implements BeanFactoryPostProcessor{

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition user = beanFactory.getBeanDefinition("user");
        MutablePropertyValues propertyValues = user.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            if (propertyValue.getValue() instanceof TypedStringValue){
                TypedStringValue value = (TypedStringValue) propertyValue.getValue();
                if (StringUtils.hasText(value.getValue())){
                    value.setValue(value.getValue().toUpperCase(Locale.ROOT));
                }
            }
        }
    }
}

@Data
class User{
    private String firstName;
    private String lastName;
}

spring-bfp.xml配置文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd"
>


    <bean id="user" class="com.buydeem.share.beanfactorypostprocessor.User">
        <property name="firstName" value="mac"/>
        <property name="lastName" value="jodn"/>
    </bean>

    <bean id="myBeanFactoryPostprocessor" class="com.buydeem.share.beanfactorypostprocessor.MyBeanFactoryPostprocessor"/>
</beans>

在配置文件中我们定义了两个Bean,其中user我们设置它的属性名字都是小写字母,而myBeanFactoryPostprocessor作为BeanFactoryPostprocessor它会自动注册到容器中,运行代码打印结果如下:

打印BeanDefinition
firstName => TypedStringValue: value [MAC], target type [null]
lastName => TypedStringValue: value [JODN], target type [null]
打印实例
User(firstName=MAC, lastName=JODN)

从运行结果可以看出,BeanDefinition中的属性值并转成了大写,这也导致了后续创建出来的实例它的属性值也变成了大写。

PropertySourcesPlaceholderConfigurer

在Spring中我们可以使用${...}的形式将properties文件中的配置替换,通常我们可以通过<property-placeholder/>来完成。如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
>


    <bean id="user" class="com.buydeem.share.beanfactorypostprocessor.User">
        <property name="firstName" value="${firstName}"/>
        <property name="lastName" value="${lastName}"/>
    </bean>
    
    <context:property-placeholder location="user.properties"/>

</beans>

实际上该标签的实现核心之一就是需要BeanFactoryPostprocessor来完成。其实我们不使用这个标签,可以使用如下配置实现相同的功能。

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:user.properties"/>
    </bean>

可能现在你还是一脸疑惑,为啥能实现${...}替换功能?我们先看一下PropertySourcesPlaceholderConfigurer结构。

Spring进阶-BeanFactoryPostprocessor
PropertySourcesPlaceholderConfigurer结构图

从上图可以看出PropertySourcesPlaceholderConfigurer它间接的实现了BeanFactoryPostprocessor接口,而前面我们的例子中我们可以通过BeanFactoryPostprocessor来更新XML中定义的BeanDefinition,而PropertySourcesPlaceholderConfigurer的实现原理也一样。同样是通过实现postProcessBeanFactory方法来替换掉${...}中的内容。

 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  if (this.propertySources == null) {
   this.propertySources = new MutablePropertySources();
            //配置环境属性
   if (this.environment != null) {
    this.propertySources.addLast(
     new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
      @Override
      @Nullable
      public String getProperty(String key) {
       return this.source.getProperty(key);
      }
     }
    );
   }
            //配置本地属性
   try {
    PropertySource<?> localPropertySource =
      new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
    if (this.localOverride) {
     this.propertySources.addFirst(localPropertySource);
    }
    else {
     this.propertySources.addLast(localPropertySource);
    }
   }
   catch (IOException ex) {
    throw new BeanInitializationException("Could not load properties", ex);
   }
  }
  //处理properties,${...}替换的主要逻辑
  processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
  this.appliedPropertySources = this.propertySources;
 }

BeanDefinitionRegistryPostProcessor

BeanFactoryPostprocessor还有一个子接口BeanDefinitionRegistryPostProcessor,该接口的定义如下:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    
 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

该接口只有一个方法,该方法提供的一个BeanDefinitionRegistry类型的参数,而BeanDefinitionRegistry提供了管理BeanDefinition的功能。例如提供了BeanDefinition注册的功能。那这个接口有啥用?例如我们可以通过该接口动态的添加BeanDefinition,废话不多说直接看示例:

public class App3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App3.class);
        Person person = context.getBean("person", Person.class);
        System.out.println(person);
    }

    @Bean
    public MyBeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(){
        return new MyBeanDefinitionRegistryPostProcessor();
    }
}

class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
                .addPropertyValue("name", "mac")
                .getBeanDefinition()
;
        registry.registerBeanDefinition("person",definition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

@Data
class Person{
    private String name;
}

上面的示例代码中,我们实现了BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,然后在该方法中添加了一个BeanDefinition。运行代码最后的打印结果如下:

Person(name=mac)

通常我们定义BeanDefinition之后再将它们添加到IOC容器,而常用的方式都是通过注解或者XML。而通过BeanDefinitionRegistryPostProcessor我们可以实现动态的将BeanDefinition添加到IOC容器中。

现在我们使用Spring几乎很少使用XML来定义BeanDefinition了,更多的是通过JavaConfig形式来完成,特别是在SpringBoot中这点更加明显。通常我们会使用到@Configuration@Bean等注解,这些注解的实现都是通过BeanDefinitionRegistryPostProcessor来实现的。关于实现的细节部分这里就不多说了,后续如果有时间再细说,有兴趣的也可以自己去看看。

小结

看完上面的内容,我认为你应该至少知道了一下几点:

  • 什么是BeanFactoryPostprocessor以及它的作用
  • 了解如何自定义BeanFactoryPostprocessor
  • PropertySourcesPlaceholderConfigurer实现的基本原理
  • BeanDefinitionRegistryPostProcessor接口的作用以及用法


原文始发于微信公众号(一只菜鸟程序员):Spring进阶-BeanFactoryPostprocessor

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72960.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!