一文吃透Spring Boot扩展之BeanFactoryPostProcessor

简介: 一文吃透Spring Boot扩展之BeanFactoryPostProcessor

概述


BeanFactoryPostProcessor是Spring中一个相当重要的扩展点,扩展点就是能让我们在Spring容器以及Bean生命周期的各个阶段中可以进行修改扩展。


什么是BeanFactoryPostProcessor


BeanFactoryPostProcessor, 翻译过来大致是Bean的工厂处理器,顾名思义,可以理解为它对Bean工厂中Bean定义(BeanDefintion)进行修改, 它的执行时机:BeanFactory标准初始化之后,所有的Bean定义已经被加载,但标准Bean的实例还没被创建(不包括BeanFactoryPostProcessor类型)。该方法通常用于修改bean的定义,Bean的属性值等,甚至可以在此快速初始化Bean。

而另外一个相关的扩展接口的BeanDefinitionRegistryPostProcessor,继承自BeanFactoryPostProcessor,所有的Bean定义即将被加载,但Bean的实例还没被创建时,也就是说,BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法执行时机先于BeanFactoryPostProcessorpostProcessBeanFactory方法。

区别于一个很类似的扩展接口BeanPostProcessor, 它的执行时机实在Bean初始化前后,添加一些自己想要的逻辑。

小结一下,上面关联的扩展接口执行顺序如下:1.BeanDefinitionRegistryPostProcessor,2.BeanFactoryPostProcessor,3.BeanPostProcessor。而BeanFactoryPostProcessor主要是在标准的BeanDefinition已经准备完毕,可以去修改已有的BeanDefinition的相关属性等。


如何使用BeanFactoryPostProcessor


  1. 新建一个测试bean
@Data
@Component
public class Student {
    @Value("${user.username:alvin}")
    private String username;
    @Value("${user.age:12}")
    private int age;
}
  1. 新建处理器实现BeanFactoryPostProcessor
@Component
@Slf4j
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("******************** TestBeanFactoryPostProcessor#postProcessBeanFactory ****************");
        log.info("******************** bean的数量:[{}] ****************", beanFactory.getBeanDefinitionCount());
        // 修改bean definition属性信息
        BeanDefinition userBeanDef = beanFactory.getBeanDefinition("student");
        userBeanDef.getPropertyValues().add("username", "cxw");
        // 快速初始化Bean
        User user = (User)beanFactory.getBean("student");
        log.info("student name: [{}]", user.getUsername());
    }
}
  1. 验证结论

1671107537937.jpg

bean的属性被成功被修改了。


源码解析


接口定义


/**
 * Factory hook that allows for custom modification of an application context's
 * bean definitions, adapting the bean property values of the context's underlying
 * bean factory.
 *
 * <p>Useful for custom config files targeted at system administrators that
 * override bean properties configured in the application context. See
 * {@link PropertyResourceConfigurer} and its concrete implementations for
 * out-of-the-box solutions that address such configuration needs.
 *
 * <p>A {@code BeanFactoryPostProcessor} may interact with and modify bean
 * definitions, but never bean instances. Doing so may cause premature bean
 * instantiation, violating the container and causing unintended side-effects.
 * If bean instance interaction is required, consider implementing
 * {@link BeanPostProcessor} instead.
 *
 * <h3>Registration</h3>
 * <p>An {@code ApplicationContext} auto-detects {@code BeanFactoryPostProcessor}
 * beans in its bean definitions and applies them before any other beans get created.
 * A {@code BeanFactoryPostProcessor} may also be registered programmatically
 * with a {@code ConfigurableApplicationContext}.
 *
 * <h3>Ordering</h3>
 * <p>{@code BeanFactoryPostProcessor} beans that are autodetected in an
 * {@code ApplicationContext} will be ordered according to
 * {@link org.springframework.core.PriorityOrdered} and
 * {@link org.springframework.core.Ordered} semantics. In contrast,
 * {@code BeanFactoryPostProcessor} beans that are registered programmatically
 * with a {@code ConfigurableApplicationContext} will be applied in the order of
 * registration; any ordering semantics expressed through implementing the
 * {@code PriorityOrdered} or {@code Ordered} interface will be ignored for
 * programmatically registered post-processors. Furthermore, the
 * {@link org.springframework.core.annotation.Order @Order} annotation is not
 * taken into account for {@code BeanFactoryPostProcessor} beans.
 *
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
@FunctionalInterface
public interface BeanFactoryPostProcessor {
  /**
   * Modify the application context's internal bean factory after its standard
   * initialization. All bean definitions will have been loaded, but no beans
   * will have been instantiated yet. This allows for overriding or adding
   * properties even to eager-initializing beans.
   * @param beanFactory the bean factory used by the application context
   * @throws org.springframework.beans.BeansException in case of errors
   */
  void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

接口的注释很清楚的说明了它的作用和细节,大致有下面几点:

  • 该接口允许用户自定义修改工厂bean中的BeanDefinition, 调整BeanDefinition的属性值,甚至初始化Bean。比如内置的PropertyResourceConfigurer,就是修改beanDefinition的属性为配置文件的属性。
  • 该接口主要是用于修改BeanDefinition, 虽然也可以直接进行实例化Bean, 但是不建议这么做,可能会造成其他未知的错误。


执行流程


image.png

其中核心逻辑是PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法,该方法的前面部分逻辑主要是处理BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法,也就是想BeanDefinition注册中中心添加新的BeanDefinition。

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
        .......... 该部分是处理BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry相关逻辑,跳过, 可以看BeanDefinitionRegistryPostProcessor的解析
        // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
        // 获取所有实现了BeanFactoryPostProcessor接口的bean name列表,前提是在BeanFactory的BeanDefinitions列表中包含对应的bean定义信息。
    String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
    // Ordered, and the rest.
        // 存放实现了PriorityOrdered接口的processor
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        // 存放实现了Ordered接口的processor
    List<String> orderedPostProcessorNames = new ArrayList<>();
        // 存放没有实现排序的processor
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        // 遍历前面全量的bean name,将他们归类,放到上面的容器中
    for (String ppName : postProcessorNames) {
            // 如果在第一阶段已经被调用过,就不调用,第一阶段主要是BeanDefinitionRegistryPostProcessor,它继承了BeanFactoryPostProcessor,它会在第一阶段调用。
      if (processedBeans.contains(ppName)) {
        // skip - already processed in first phase above
      }
      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);
      }
    }
    // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
        // 首先执行实现了PriorityOrdered接口的processor,对它们进行排序
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        // 真实执行processor中的逻辑。
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        // 其次执行实现了Ordered接口的processor,对它们进行排序后执行processor中的逻辑。
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String postProcessorName : orderedPostProcessorNames) {
      orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    // 最后执行,没有顺序要求的processor
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String postProcessorName : nonOrderedPostProcessorNames) {
      nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    // Clear cached merged bean definitions since the post-processors might have
    // modified the original metadata, e.g. replacing placeholders in values...
    beanFactory.clearMetadataCache();
  }
//调用processors中的postProcessBeanFactory方法
private static void invokeBeanFactoryPostProcessors(
      Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
      StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
          .tag("postProcessor", postProcessor::toString);
      postProcessor.postProcessBeanFactory(beanFactory);
      postProcessBeanFactory.end();
    }
  }


小结


整个执行流程的源码还是比较清晰并且简单的。重点提下下面两个点:

  1. 我们自定义的BeanFactoryProcessor需要加上 @Component等注解,为什么?

看源码得知,String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);获取所有实现了BeanFactoryPostProcessor的Bean Name, 前提所有的Bean都要被注册到BeanDefinitionRegistry, 通过添加@Component, @Service等注解,可以将对应的Bean定义信息注册到BeanFactory中,方便后面实例化Bean。 那么它是在什么地方注册的呢?可以看下ConfigurationClassPostProcessor类,它实现了BeanDefinitionRegistryPostProcessor,会扫描所有@Component, @Service等注解,将对应的Bean Definition注册到BeanFactory中。

  1. 执行顺序问题

我们可以通过实现PriorityOrdered, Ordered接口,控制BeanFactoryProcessor的执行顺序,, 优先执行实现了PriorityOrdered接口,其次是Ordered,最后是没有实现任何排序接口的processor。


内置的BeanFactoryPostProcessor


Spring内置了一个比较重要的BeanFactoryPostProcessorPlaceholderConfigurerSupport, 实现从配置文件中获取属性。

1671107626544.jpg

目录
相关文章
|
6天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
23 2
|
2月前
|
Java 开发者 Spring
"揭秘SpringBoot魔法SPI机制:一键解锁服务扩展新姿势,让你的应用灵活飞天!"
【8月更文挑战第11天】SPI(Service Provider Interface)是Java的服务提供发现机制,用于运行时动态查找和加载服务实现。SpringBoot在其基础上进行了封装和优化,通过`spring.factories`文件提供更集中的配置方式,便于框架扩展和组件替换。本文通过定义接口`HelloService`及其实现类`HelloServiceImpl`,并在`spring.factories`中配置,结合`SpringFactoriesLoader`加载服务,展示了SpringBoot SPI机制的工作流程和优势。
47 5
|
4月前
|
监控 Java 应用服务中间件
Spring Boot应用的部署与扩展
Spring Boot应用的部署与扩展
|
4月前
|
运维 Java 关系型数据库
Spring运维之boot项目bean属性的绑定读取与校验
Spring运维之boot项目bean属性的绑定读取与校验
49 2
|
4月前
|
存储 运维 Java
Spring运维之boot项目开发关键之日志操作以及用文件记录日志
Spring运维之boot项目开发关键之日志操作以及用文件记录日志
53 2
|
4月前
|
Java Maven
springboot项目打jar包后,如何部署到服务器
springboot项目打jar包后,如何部署到服务器
286 1
|
4月前
|
XML 运维 Java
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
43 1
|
3月前
|
监控 Java 应用服务中间件
Spring Boot应用的部署与扩展
Spring Boot应用的部署与扩展
|
4月前
|
存储 Java Apache
整合Spring Boot和Pulsar实现可扩展的消息处理
整合Spring Boot和Pulsar实现可扩展的消息处理