由BeanFactoryPostProcessor想到

在看Ngbatis源码时看到了对BeanFactoryPostProcessor后置处理器的使用,对其的使用并不是很了解,在此做一些学习和总结。

1. 相关类

  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor
  • BeanPostProcessor

2. BeanFactoryPostProcessor

2.1. 作用

BeanFactoryPostProcessor 的主要目的是在Bean实例化前对Bean的配置元数据,也就是对BeanDefinition进行修改操作。

在 Spring 中,所有的 Beans 在被完全实例化之前都是以 BeanDefinition 的形式存在的。

BeanFactoryPostProcessor 能够在Bean完全实例化之前调整和修改这些 BeanDefinition,对 BeanDefinition 的任何修改都会影响后续的 Bean 实例化和初始化过程。

2.2. 加载与执行

方式有两种:

  1. 利用 ConfigurableApplicationContext 接口的 addBeanFactoryPostProcessor 方法。

    Ngbatis 的加载方式就是利用第一种,在 ApplicationContextInitializer 接口的实现类的 initialize 方法中直接调用 addBeanFactoryPostProcessor 。关于 ApplicationContextInitializer 接口的使用和原理,可以参考上一篇文章: ApplicationContextInitializer接口类的使用和原理解读。

  2. org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法中扫描实现了 BeanFactoryPostProcessor 接口的 Bean 自动加载。

BeanFactoryPostProcessor 的加载调用涉及到了十分核心的 org.springframework.context.support.AbstractApplicationContext#refresh --> org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors 方法。

第二种方式的调用实现用一张时序图来表现:

2.2.1 扩展知识

BeanFactoryPostProcessor 的加载得对 AbstractApplicationContext 的有关接口进行一个介绍。

放一张UML图在这里:

可以看到, AbstractApplicationContext 实现了 ConfigurableApplicationContext 接口,ConfigurableApplicationContext 接口又继承自 ApplicationContext,ApplicationContext 又扩展了 BeanFactory 的功能。它们之间的关系如上图。归根结底都是帮助我们在管理 Bean。

因为这里这部分不是重点内容,所以只简单说一下他们的作用与区别:

  • BeanFactory:BeanFactory 与 ApplicationContext 存在派生关系,它是 IOC 的核心接口,虽然没有具体实现但定义了 IOC 容器最基本的规范,其他具体的容器都是通过实现 BeanFactory 的基础上,再进行功能扩展。

    BeanFactory 是典型的工厂模式的工厂接口。从它的方法中可以看出只提供了获取和判断的方法。

  • ApplicationContext:在 BeanFactory 的基础上进行了功能扩展,除了间接实现了 BeanFactory 还实现了其他的接口,提供了更多面向实际应用的功能,比如国际化、监听器等。是一个更高级的容器。

  • ConfigurableApplicationContext:ConfigurableApplicationContext 是 ApplicationContext 的子接口,它在 ApplicationContext 的基础上添加了一系列配置应用上下文的功能。里面就包括了,接下来会说的很重要的添加后置处理器的方法:addBeanFactoryPostProcessor

  • AbstractApplicationContext:实现了 ConfigurableApplicationContext 接口,且通过模板方法的设计模式,实现了所有应用上下文的通用方法,其中包括了十分重要的 refresh() 方法,是很重要的一个类。里面实现了包括最重要的容器初始化操作 refresh 方法、Bean 的生成和获取、监听器的添加等等。因为这个类的方法太多了,在此就不贴图了,有兴趣的小伙伴可以自行查看源码。

3. BeanDefinitionRegistryPostProcessor

3.1. 作用

BeanDefinitionRegistryPostProcessor 是 Spring 容器的一个扩展点,继承自 BeanFactoryPostProcessor,它提供了更为深入的方式来干预bean定义的注册过程。主要用于在 Spring 容器完成对 Bean 的定义信息的加载后、但在它们真正实例化之前,进行额外的操作。

它的特殊之处在于,除了能够像 BeanFactoryPostProcessor 那样修改已经注册的bean定义(BeanDefinition),还能向注册中心 BeanDefinitionRegistry 中动态地添加或移除bean定义。

可以看下 BeanDefinitionRegistryPostProcessor 的源码:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	/**
* 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;
}

从源码注释中可以看出来,它的执行时机是在 BeanFactoryPostProcessor 之前的阶段,对 Bean 的定义信息进行一些操作。

3.2. 加载与执行

BeanDefinitionRegistryPostProcessor 的加载与执行时序图与 BeanFactoryPostProcessor 差不多,在此基础上再加点内容就好。

从时序图中可以看到,BeanDefinitionRegistryPostProcessor 的执行在 BeanFactoryPostProcessor 之前,这也是他们的关键区别。另一个关键区别就是 BeanDefinitionRegistryPostProcessor 可以对 Bean 定义进行增删改操作,而 BeanFactoryPostProcessor 只能对 Bean 定义进行修改操作。

4. BeanPostProcessor

4.1. 作用

BeanPostProcessor是对bean实例生效的,相对于对BeanDefinition的处理,这个阶段更加靠后,BeanPostProcessor处理的时候已经生成了实例对象,BeanPostProcessor会在对象的实例基础上进行一个更进一步的加工。

BeanPostProcessor除了能对Bean进行深加工外,还能直接进行Bean替换,Spring AOP的功能就是这样实现的,把经过代理的Bean放了进去,替换了原有的Bean。

5. 区别与对比

三种后期处理器的执行时机用一张流程图来表示:

三种后置处理器的对比

后置处理器 处理目标 执行时机 主要操作
BeanDefinitionRegistryPostProcessor 针对BeanDefinitionRegistry(来自各种配置源) 在所有BeanDefinition被加载和注册之后,在BeanFactoryPostProcessor执行之前 向BeanDefinitionRegistry注册、修改、删除BeanDefinition
BeanFactoryPostProcessor 针对BeanDefinition(Bean的配置元数据) 在所有BeanDefinition被加载和注册之后,在Bean实例化和初始化之前 修改已注册的BeanDefinition,包括修改属性、改变类的定义等
BeanPostProcessor 针对已经实例化,但未完全初始化的Bean对象 在Bean初始化过程中,具体是在Bean生命周期的初始化方法前后执行 主要用于在Bean初始化前后,比如修改Bean属性,生成代理对象等。

[Ngbatis源码学习][SpringBoot] 由BeanFactoryPostProcessor想到的更多相关文章

  1. SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

    系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...

  2. SpringBoot源码学习3——SpringBoot启动流程

    系列文章目录和关于我 一丶前言 在 <SpringBoot源码学习1--SpringBoot自动装配源码解析+Spring如何处理配置类的>中我们学习了SpringBoot自动装配如何实现 ...

  3. Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

    Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...

  4. 源码学习系列之SpringBoot自动配置(篇一)

    源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...

  5. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  6. SpringBoot源码学习系列之SpringMVC自动配置

    目录 1.ContentNegotiatingViewResolver 2.静态资源 3.自动注册 Converter, GenericConverter, and Formatter beans. ...

  7. SpringBoot源码学习系列之异常处理自动配置

    SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...

  8. SpringBoot源码学习系列之嵌入式Servlet容器

    目录 1.博客前言简单介绍 2.定制servlet容器 3.变换servlet容器 4.servlet容器启动原理 SpringBoot源码学习系列之嵌入式Servlet容器启动原理 @ 1.博客前言 ...

  9. SpringBoot源码学习系列之启动原理简介

    本博客通过debug方式简单跟一下Springboot application启动的源码,Springboot的启动源码是比较复杂的,本博客只是简单梳理一下源码,浅析其原理 为了方便跟源码,先找个Ap ...

  10. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

随机推荐

  1. springboot启动流程 (1) 流程概览

    本文将通过阅读源码方式分析SpringBoot应用的启动流程,不涉及Spring启动部分(有相应的文章介绍). 本文不会对各个流程做展开分析,后续会有文章介绍详细流程. SpringApplicati ...

  2. JS - Array - 在数组的指定下标添加或替换元素 。 也可删除指定下标的元素

    一,首先介绍下 js Array对象 中的 splice 方法 . ( splice在英文中是剪接的意思 ) 1,定义和用法 splice() 方法用于插入.删除或替换数组的元素. 注意:这种方法会改 ...

  3. [转帖]SCSI、ISCSI、iSER、NVMe、NVMe-oF、NVMe-oF over RDMA

    在存储系统中,上层协议可以泛指"指令",也就是比如"读出从某某开始的多少长度的扇区",指令包含三大关键信息: (1)操作码:Opreation Code,或称为 ...

  4. [转帖]基于MySQL8.0存储过程实现myawr平台的top sql功能

    概述 众所周知,MySQL数据库中的performance_schema的事件统计表中的统计数据计算的是累计值,如果想要计算某段时间的TOP SQL是不行的,这里考虑用函数定期取值存进中间表定期将累计 ...

  5. [转帖]《Linux性能优化实战》笔记(三)—— CPU 上下文切换(下)

    上篇介绍了三种CPU 上下文切换以及它们可能造成的问题和原因,这一篇来看看在系统中如何发现CPU 上下文切换问题. 一. 查看上下文切换情况 主要使用两个命令:vmstat以及之前用过的pidstat ...

  6. Nginx双层域名时 iframe嵌入/跳转页面的处理过程

    Nginx双层域名时 iframe嵌入/跳转页面的处理过程 背景 两年前在上一家公司内遇到一个Nginx的问题 当时的场景是 双层nginx代理时(一层域名侧, 一层拆分微服务的网关层) 程序里面会打 ...

  7. [转帖]win10下使用Rclone将OneDrive映射到本地磁盘教程(开机自动挂载)

    win10下使用Rclone将OneDrive映射到本地磁盘教程(开机自动挂载) 下载rclone,winfsp和Git bash Rclone. Winfsp. 和 [Git bash](https ...

  8. [转帖]iptables开放指定端口

    https://www.jianshu.com/p/5b44dd20484c 由于业务的需要, MySQL,Redis,mongodb等应用的端口需要我们手动操作开启 下面以 MySQL 为例,开启 ...

  9. Springboot tomcat.threads线程数学习

    Springboot tomcat.threads线程数学习 摘要 压测完nginx 突然想搞一下springboot内嵌的tomcat的 threads的参数 一不做二不休, 直接就着脚本进行学习和 ...

  10. [转贴]细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4

    细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4 https://www.cnblogs.com/malecrab/p/5300503.html 1. U ...