[Ngbatis源码学习][SpringBoot] 由BeanFactoryPostProcessor想到
由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. 加载与执行
方式有两种:
利用
ConfigurableApplicationContext接口的addBeanFactoryPostProcessor方法。Ngbatis 的加载方式就是利用第一种,在 ApplicationContextInitializer 接口的实现类的 initialize 方法中直接调用 addBeanFactoryPostProcessor 。关于 ApplicationContextInitializer 接口的使用和原理,可以参考上一篇文章: ApplicationContextInitializer接口类的使用和原理解读。
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想到的更多相关文章
- SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...
- SpringBoot源码学习3——SpringBoot启动流程
系列文章目录和关于我 一丶前言 在 <SpringBoot源码学习1--SpringBoot自动装配源码解析+Spring如何处理配置类的>中我们学习了SpringBoot自动装配如何实现 ...
- Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题
Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...
- 源码学习系列之SpringBoot自动配置(篇一)
源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...
- 源码学习系列之SpringBoot自动配置(篇二)
源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...
- SpringBoot源码学习系列之SpringMVC自动配置
目录 1.ContentNegotiatingViewResolver 2.静态资源 3.自动注册 Converter, GenericConverter, and Formatter beans. ...
- SpringBoot源码学习系列之异常处理自动配置
SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...
- SpringBoot源码学习系列之嵌入式Servlet容器
目录 1.博客前言简单介绍 2.定制servlet容器 3.变换servlet容器 4.servlet容器启动原理 SpringBoot源码学习系列之嵌入式Servlet容器启动原理 @ 1.博客前言 ...
- SpringBoot源码学习系列之启动原理简介
本博客通过debug方式简单跟一下Springboot application启动的源码,Springboot的启动源码是比较复杂的,本博客只是简单梳理一下源码,浅析其原理 为了方便跟源码,先找个Ap ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
随机推荐
- 16-集电极开路门(OC门)
集电极开路门(OC门) OC门 两个与非门,要实现非,一般来讲再与一下就可以. 能不能将输出端并在一起?普通的门电路永远不可能输出端并在一起,连在一起的. TTL与非门输出端连在一起 集电极断开之后连 ...
- Vue - 父子级的相互调用
父级调用子级 父级: <script> this.$refs.child.load(); 或 this.$refs.one.load(); </script> 子级: < ...
- [转帖]java乱码 java使用的编码是utf-8还是utf-16还是unicode
简单介绍编码 Unicode:是容纳世界所有文字符号的国标标准编码,使用四个字节为每个字符编码 UTF:是英文 Unicode Transformation Format 的缩写,意为把 Unicod ...
- [转帖]OceanBase 存储引擎详解
https://zhuanlan.zhihu.com/p/436485359 作者简介:沈炼,蚂蚁集团技术风险部数据库高级专家毕业于东南大学,2014年以来从事 OceanBase 在蚂蚁的架构工作, ...
- [转帖]Linux查看raid1和raid10分别由哪些盘组成,在哪个槽位
查找有问题的盘 MegaCli64 -PDList -aALL |grep "Firmware state" 6个盘,2个坏了 查看raid级别和硬盘的状态 MegaCli64 - ...
- 使用TFS CI 又想保留服务运行状态的简单方法
最近公司使用TFS-CI的方式定期部署测试环境, 但是发现TFS-CI 运行完之后会清理agent所在的测试环境. 运行的进程都会被killed 1. 第一种方法: 本来第一反应 是使用systemd ...
- 公司内部自建DNS的办法 使用私有域名的方法
最近总是有一个需求,需要自己弄一些服务器域名之类的. 修改hosts总是比较麻烦,所以想了一个简单办法, 自己搭建一个dns服务器, 本来想用最简单的 dnsmasq 但是发现总是不成功, 然后找了另 ...
- 庖丁解牛:最全babel-plugin-import源码详解
庖丁解牛:最全 babel-plugin-import 源码详解 序言:在用 babel-plugin 实现按需加载一文中笔者用作用域链思路实现了按需加载组件.此思路是统一式处理,进入 ImportD ...
- 每日一道Java面试题:方法重载与方法重写,这把指定让你明明白白!
写在开头 请聊一聊Java中方法的重写和重载? 这个问题应该是各大厂面试时问的最多的话题之一了,它们几乎贯穿了我们日常的开发工作,在过往的博客中我们多多少少都提到过重载与重写,而今天我们就一起来详细的 ...
- 【杂题,树】【Uoj】Uoj618 【JOISC2021】聚会 2
2023.7.3 Problem Link 给定一棵 \(n\) 个点的树,对于一个点集 \(S\),定义 \(f(u,S)\) 为 \(\min_u \sum_{v\in S} \mathrm{di ...