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

目录
相关文章
|
2天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
1天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
22天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
42 0
|
Java Spring 容器
Spring之BeanPostProcessor(后置处理器)介绍
为了弄清楚Spring框架,我们需要分别弄清楚相关核心接口的作用,本文来介绍下BeanPostProcessor接口
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
244 2
|
2天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
9天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
53 14
|
1月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
49 1
SpringBoot入门(7)- 配置热部署devtools工具
|
1月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
47 2
 SpringBoot入门(7)- 配置热部署devtools工具