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

目录
相关文章
|
18天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
10天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
22 0
|
1月前
|
监控 Java 开发者
BeanPostProcessor:Spring框架的灵活扩展机制
【10月更文挑战第4天】在Spring框架中,BeanPostProcessor接口是一个非常重要的扩展点,它允许开发者在Spring容器实例化、依赖注入以及初始化Bean的过程中插入自定义逻辑。
60 0
|
30天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
65 0
|
1月前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
45 0
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
5月前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
46 1
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
210 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
5月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
62 0
|
3月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)