深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor

简介: 深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor

1. BeanFactoryPostProcessor


首先说BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,先来看BeanFactoryPostProcessor:


/**
 * 1. Allows for custom modification of an application context's bean definitions,
 * adapting the bean property values of the context's underlying bean factory.
 *
 * 2. <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
 * their bean definitions and apply them before any other beans get created.
 *
 * 3. <p>Useful for custom config files targeted at system administrators that
 * override bean properties configured in the application context.
 *
 * 4. <p>See PropertyResourceConfigurer and its concrete implementations
 * for out-of-the-box solutions that address such configuration needs.
 *
 * 5. <p>A 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.
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {
  /**
   * 6. 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;
}


这次你肯定会说,你TM逗我玩?没有中文注释看个蛋蛋?我不给注释的原因是本来源码中人家英文注释已经说的很清楚这玩意是干嘛的,你看不懂的话别怕,我给你一句一句翻译好了,顺便穿插上我自己的理解😊。

我们来看最重点的这块的注释,就是第6处的,这些英文翻译过来的意思是:在ApplicationContext里面的beanFactory对象进行标准的初始化之前修改它,此时所有的BeanDefinition应该已经被加载了,但是还没有bean被初始化。这个方法允许修改或者增加beanDefinition的属性,甚至是初始化bean。好了,6处的我已经翻译完了。我们经过对之前文章的学习,已经知道了在执行BeanFactoryPostProcessor之前就已经把所有的BeanDefinition加载进来了,对照着我对注释的翻译不难理解,我们可以去修改已经加载的BeanDefinition的信息。有的人肯定还想骚一下,在这里让某个bean提前初始化,但是请收起你的骚,为什么呢?我们来看看第5处的注释。

这段英文翻译过来的意思是:一个BeanFactoryPostProcessor可能会和其他的beanDefinition做交互或者是修改它,但是绝不是和bean去做这些事情。因为和bean做这些事情会触发bean过早的初始化,这是对容器的侵入并且会导致一些副作用,如果需要bean实例的交互,可以考虑实现BeanPostProcessor。好了,我翻译完了,你肯定还是不理解为啥不能在这里初始化。我们看看我下面这个demo:



@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        TestBean testBean = beanFactory.getBean(TestBean.class);
    }
}


TestBean.java


@Component
@Data
public class TestBean {
    @Autowired
    private TestBean1 testBean1;
}


TestBean2.java


@Component
public class TestBean1 {
}


我们自己定义了一个BeanFactoryPostProcessor,然后里面进行了TestBean的实例化,接着启动项目,我们可以在控制台中看到:


1686811229604.png


也就是说,TestBean进行了初始化,但是,它的成员属性却是null,这是因为进行属性注入的BeanPostProcessor还没有初始化,而且,直到最后,这个成员属性也一直是null,这个是在使用注解的情况下会有这样的事情发生,如果是使用的是xml,则不会有这样的事情,但是还是请不要这么做。

然后我们再说说BeanDefinitionRegistryPostProcessor,还是来看看代码哈:


/**
 * 1. Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
  /**
   * 2. Modify the application context's internal bean definition registry after its
   * standard initialization. All regular bean definitions will have been loaded,
   * but no beans will have been instantiated yet. This allows for adding further
   * bean definitions before the next post-processing phase kicks in.
   * @param registry the bean definition registry used by the application context
   * @throws org.springframework.beans.BeansException in case of errors
   */
  void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}


我们来看第二处的翻译过来的意思:在ApplicationContext的标准初始化之后去修改它的BeanDefinition的注册。此时所有的普通的BeanDefinition都已经被加载但是还没有被初始化,这里可以在下个处理阶段之前添加更多的BeanDefinition。我们大家仔细看看这里的这个方法参数,然后再对比一下这个方法参数和BeanFactoryPostProcessor里面那个方法参数,不难发现,这两个方法的参数是不一样的,其中BeanDefinitionRegistryPostProcessor里面的方法参数是BeanDefinitonRegistry,但是BeanFactoryPostProcessor里面的却是ConfigurableListableBeanFactory,通过对比这两个参数,我们可以得出来这样的结论,BeanDefinitionRegistryPostProcessor是为了添加BeanDefinition,而BeanFactoryPostProcessor是为了修改BeanDefinition的属性,在作者看来,这两个接口都是基于框架层面的扩展,什么叫做基于框架的扩展呢,我认为,一般来说,当你需要使用到这两个接口的时候,说明你是要基于spring框架去扩展一些东西,这时候,你写出的类在业务代码中是不会使用的,而且你的代码的运行,只是在spring  ioc容器初始化的这个过程中,此时业务类都还没有初始化。有的扩展点,比如你实现了InitializingBean这种接口,这时候你实现这个接口的代码很可能会在业务流程中使用,这种的扩展点我们可以理解它为基于应用层面的扩展,我们之前的自定义xml标签,也是属于基于框架层面的扩展。

我们最后来看个BeanDefinitionRegistryPostProcessor的案例吧,我们使用mybatis的时候,经常会使用Mapper注解标在一个接口上,然后会在另外一个类中通过Autowired注解去引用,这时候,其实接口根本就没有实现类,但是我们Autowired还是可以引用成功,其实就是因为mybatis-spring模块中的MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor这个接口,增加了BeanDefinition到beanFactory中,我们来看看源码:



@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }


这段代码是MapperScannerConfigurer中对BeanDefinitionRegistryPostProcessor的实现,最后这句代码,即为扫描basePackage路径下的Mapper,并解析成对应的BeanDefinition到spring容器中。


2. BeanPostProcessor


BeanPostProcessor这个是Spring提供给我们的另外一个扩展的接口,这个接口的作用是在bean已经创建之后去修改bean里面的信息,主要是修改属性。我们来看看它的代码:


public interface BeanPostProcessor {
  Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}


可以看到,里面也不过只有短短的两行代码而已。其中上面的方法postProcessBeforeInitialization是在bean创建出来并且初始化之前spring帮你调用的,后边的方法是在bean创建出来之后并且初始化了之后spring帮你调用的。很多人搞不懂这个初始化具体指的是啥,这个初始化指的是:bean在创建好并且被填充好了之后,但是还没做任何的初始化操作,换句话说这时候你的bean中的成员属性已经被注入了,但是可能你还需要这个成员属性去做的一些初始化操作,还没有做。

关于这个类的使用场景需要读者自己去研究,我这里仅仅提醒一点,同样的,在作者看来,这个类也是基于框架的扩展,所以也不要在这个类的实现里面做任何的业务逻辑操作,道理和上面的BeanFactoryPostProcessor相同,这时候,你的业务代码中的bean很可能是null,如果需要初始化的一些业务逻辑操作,请使用其他的初始化的手段。

目录
相关文章
|
4天前
|
XML Java 数据格式
Spring框架入门:IoC与DI
【5月更文挑战第15天】本文介绍了Spring框架的核心特性——IoC(控制反转)和DI(依赖注入)。IoC通过将对象的创建和依赖关系管理交给容器,实现解耦。DI作为IoC的实现方式,允许外部注入依赖对象。文章讨论了过度依赖容器、配置复杂度等常见问题,并提出通过合理划分配置、使用注解简化管理等解决策略。同时,提醒开发者注意过度依赖注入和循环依赖,建议适度使用构造器注入和避免循环引用。通过代码示例展示了注解实现DI和配置类的使用。掌握IoC和DI能提升应用的灵活性和可维护性,实践中的反思和优化至关重要。
18 4
|
4天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
4天前
|
XML Java 程序员
Spring特性之二——IOC控制反转
Spring特性之二——IOC控制反转
16 4
|
4天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
24 0
|
4天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
20 0
|
4天前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
35 2
|
4天前
|
Java 测试技术 数据库连接
Spring中ioc的优点
总之,Spring中的IoC提供了一种更加灵活、可维护、可测试和可扩展的方式来管理组件之间的依赖关系,从而提高了应用程序的质量和可维护性。这使得开发人员能够更专注于业务逻辑而不是底层的技术细节。
32 1
|
存储 Java 程序员
深入理解 Spring BeanPostProcessor
回顾上一篇博客中,在AbstractApplicationContext这个抽象类中,Spring使用invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor,通过回调Spring自己添加的ConfigurationClassPostProcessor以及用户添加的bean工厂的后置处理器,完成了包扫描以及对主配置类代理的工作 本篇博文将继续往下跟进
94 0
|
4天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
60 0
|
4天前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
142 0