深入理解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,如果需要初始化的一些业务逻辑操作,请使用其他的初始化的手段。

目录
相关文章
|
27天前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
27天前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
27天前
|
XML Java 数据格式
Spring5入门到实战------2、IOC容器底层原理
这篇文章深入探讨了Spring5框架中的IOC容器,包括IOC的概念、底层原理、以及BeanFactory接口和ApplicationContext接口的介绍。文章通过图解和实例代码,解释了IOC如何通过工厂模式和反射机制实现对象的创建和管理,以及如何降低代码耦合度,提高开发效率。
Spring5入门到实战------2、IOC容器底层原理
|
27天前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
21天前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
2月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
95 0
|
14天前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
26 0
|
1月前
|
XML Java 数据库连接
Spring Boot集成MyBatis
主要系统的讲解了 Spring Boot 集成 MyBatis 的过程,分为基于 xml 形式和基于注解的形式来讲解,通过实际配置手把手讲解了 Spring Boot 中 MyBatis 的使用方式,并针对注解方式,讲解了常见的问题已经解决方式,有很强的实战意义。在实际项目中,建议根据实际情况来确定使用哪种方式,一般 xml 和注解都在用。
|
2月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
78 11