Spring源码九:BeanFactoryPostProcessor
上一篇Spring源码八:容器扩展一,我们看到ApplicationContext容器通过refresh方法中的prepareBeanFactory方法对BeanFactory扩展的一些功能点,包括对SPEL语句的支持、添加属性编辑器的注册器扩展解决Bean属性只能定义基础变量的问题、以及一些对Aware接口的实现。
这一篇,我们回到refresh方法中,接着往下看:
{synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing. 1、初始化上下文信息,替换占位符、必要参数的校验prepareRefresh();// Tell the subclass to refresh the internal bean factory. 2、解析类Xml、初始化BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计// Prepare the bean factory for use in this context. 3、准备BeanFactory内容:prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:try {// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器//!!!!!!!!!!!! 这里 这里 今天看这里 !!!!!!!!!!!//postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context. 5、spring bean容器的后置处理器invokeBeanFactoryPostProcessors(beanFactory);//!!!!!!!!!!!! 这里 这里 今天看这里 !!!!!!!!!!!//// Register bean processors that intercept bean creation. 6、注册bean的后置处理器registerBeanPostProcessors(beanFactory);// Initialize message source for this context. 7、初始化消息源initMessageSource();// Initialize event multicaster for this context. 8、初始化事件广播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展onRefresh();// Check for listener beans and register them. 10、初始化监听器registerListeners();// Instantiate all remaining (non-lazy-init) singletons. 11、实例化:非兰加载BeanfinishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event. 12、发布相应的事件通知finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}
工厂后置处理方法:postProcessBeanFactory
上一篇我们看到了方法prepareBeanFactory方法,我们接着这个方法往下可以看到postProcessBeanFactory,进入该方法如下:
没错,又是一个空实现,看到这里我的风格都会说:扩展点加一;postProcessBeanFactory方法使用保护修饰且留给子类自己去具体实现。结合注释我们可以简单猜测一下,该方法是Spring暴露给子类去修改初级容器BeanFactory的。
那到底是什么时候可以允许子类对BeanFactory容器进行修改呢?通过方法在refresh方法的位置在prepareBeanFactory方法之后,说明是在BeanDefiniton都已经注册到BeanFactory以后,但是还未进行实例化的时候进行修改。
我们在前面8篇内容,一点点拨开Spring的方法,到这里其实我们的进程,仅仅进行到将xml文件中的Bean标签内容解析成BeanDefinition对象然后注入到Spring的BeanFactory容器里而已,这个和我们真正意义上的Bean还有一段距离呢。BeanDefintiion从名字上来看是Bean的定义,解释下应该算是Bean属性信息的初始化。
而我们在Java代码里面使用的Bean的实例化对象,需要利用我们BeanDefinition定义的属性信息去创建我们的实例化对象。在Spring中最常见的一个场景就是我们通过Spring容器getBean方法获取一个实例。这个过程我们经常称之为bean的实例化。
所以方法postBeanFactory在Bean初始化以后实例化之前为我们提供了一个可以修改BeanDefinition的机会,我们可以通过实现postBeanFactory方法去修改beanFactory的参数。
重写postProcessBeanFactory示例
package org.springframework;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author Jeremy* @version 1.0* @description: 自定义容器* @date 2024/7/1 20:17*/
public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {/*** Create a new ClassPathXmlApplicationContext, loading the definitions* from the given XML file and automatically refreshing the context.* @param configLocations resource location* @throws BeansException if context creation failed*/public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {super(configLocations);}/**** Spring refresh 方法第扩展点加一:refresh方法中的prepareRefresh();* 重写initPropertySources 设置环境变量和设置requiredProperties**/@Overrideprotected void initPropertySources() {System.out.println("自定义 initPropertySources");getEnvironment().getSystemProperties().put("systemOS", "mac");getEnvironment().setRequiredProperties("systemOS");}/**** Spring refresh 方法第扩展点加一:refresh方法中的postProcessBeanFactory();* 重写该方法,修改基础容器BeanFactory内容* @param beanFactory**/@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {System.out.println("自定义 postProcessBeanFactory");// 获取容器中的对象BeanDefinition beanDefinition = beanFactory.getBeanDefinition("jsUser");beanDefinition.getScope();// 修改属性beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);}
}
如上述方法,我们重写了postProcessorBeanFactory方法,通过beanFactory参数获取Bean Definition对象,然后修改BeanDefinition属性值。当让我还可以修改BeanFactory的其他属性,因为这个参数是将整个BeanDefinition委托给我们自己定义的。
package org.springframework;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dto.JmUser;/*** @author Jeremy* @version 1.0* @description: TODO* @date 2024/6/30 02:58*/
public class Main {public static void main(String[] args) {ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");JmUser jmUser = (JmUser)context.getBean("jmUser");System.out.println(jmUser.getName());System.out.println(jmUser.getAge());}
}
运行上述代码,可以看到结果如上图所示,Spring的postProcessorBeanFactory有回调我自定义的方法。正因为Spring在这立留了钩子函数,所以很多框架可以通过这个方法或有类似功能的扩展点来操作整个beanFactory容器,从而可以自定义自己的框架使之拥有更加丰富与个性化的功能。后面会单独留一篇用来说Spring的扩展点,接口SpringCloud相关组件来来看看。
invokeBeanFactoryPostProcessors
看到这里其实今天的核心内容已经结束了,但是我们在文章开始的时候给大家圈里了两个方法,这里我们在提一下和上述方法功能上一致的一个类。
/*** 实例化并调用所有已经注册BeanFactoryPostProcessor* Instantiate and invoke all registered BeanFactoryPostProcessor beans,* respecting explicit order if given.* <p>Must be called before singleton instantiation.*/protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {//PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}/*** Return the list of BeanFactoryPostProcessors that will get applied* to the internal BeanFactory.*/public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {return this.beanFactoryPostProcessors;}
在上述代码里的注释里面,已经解释了这个代码的功能是为了实例化并调用所有已经注册的BeanFactoryPostProcessor。所以能知道我这个放的核心其实是BeanFactoryPostProcessor,进入BeanFactoryPostProcessor类看下:
/** Copyright 2002-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;/*** Factory hook that allows for custom modification of an application context's* bean definitions, adapting the bean property values of the context's underlying* bean factory.** <p>Useful for custom config files targeted at system administrators that* override bean properties configured in the application context. See* {@link PropertyResourceConfigurer} and its concrete implementations for* out-of-the-box solutions that address such configuration needs.** <p>A {@code 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.** <h3>Registration</h3>* <p>An {@code ApplicationContext} auto-detects {@code BeanFactoryPostProcessor}* beans in its bean definitions and applies them before any other beans get created.* A {@code BeanFactoryPostProcessor} may also be registered programmatically* with a {@code ConfigurableApplicationContext}.** <h3>Ordering</h3>* <p>{@code BeanFactoryPostProcessor} beans that are autodetected in an* {@code ApplicationContext} will be ordered according to* {@link org.springframework.core.PriorityOrdered} and* {@link org.springframework.core.Ordered} semantics. In contrast,* {@code BeanFactoryPostProcessor} beans that are registered programmatically* with a {@code ConfigurableApplicationContext} will be applied in the order of* registration; any ordering semantics expressed through implementing the* {@code PriorityOrdered} or {@code Ordered} interface will be ignored for* programmatically registered post-processors. Furthermore, the* {@link org.springframework.core.annotation.Order @Order} annotation is not* taken into account for {@code BeanFactoryPostProcessor} beans.** @author Juergen Hoeller* @author Sam Brannen* @since 06.07.2003* @see BeanPostProcessor* @see PropertyResourceConfigurer*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {/*** 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;}
看到这个接口只有一个方法且名称也叫postProcessBeanFactory,在看代码的上面的这是与参数和我们refresh中一样,说明我们也可以通过实现Bean FactoryPostProcessor接口的形式来修改我们的BeanFactory对象。如下所示:
自定义BeanFactoryPostProcessor
package org.springframework;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;/*** 自定义Bean的后置处理器*/
public class MySelfBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {System.out.println("自定义Bean的后置处理器,重写postProcessBeanFactory方法");// 获取容器中的对象BeanDefinition beanDefinition = beanFactory.getBeanDefinition("jmUser");beanDefinition.getScope();// 修改属性beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);}
}
Spring已经提供了空实现的postProcessorBeanFactory方法,为啥还要单独搞一个接口来定一个这个方法呢?
Spring提供BeanFactoryPostProcessor
接口,而不是在refresh
方法中直接进行beanFactory修改,主要是为了提高代码的解耦性、可维护性和扩展性。通过将不同的修改操作分散到不同的实现类中,每个类都只关注特定的任务,这样不仅符合单一职责原则,还使得整个框架更加灵活和易于扩展。这种设计理念是
Spring成功的重要原因之一。
整体小结
相关文章:

Spring源码九:BeanFactoryPostProcessor
上一篇Spring源码八:容器扩展一,我们看到ApplicationContext容器通过refresh方法中的prepareBeanFactory方法对BeanFactory扩展的一些功能点,包括对SPEL语句的支持、添加属性编辑器的注册器扩展解决Bean属性只能定义基础变量的问题、以及一些…...

大模型笔记1: Longformer环境配置
论文: https://arxiv.org/abs/2004.05150 目录 库安装 LongformerForQuestionAnswering 库安装 首先保证电脑上配置了git. git环境配置: https://blog.csdn.net/Andone_hsx/article/details/87937329 3.1、找到git安装路径中bin的位置,如:D:\Prog…...

类和对象(提高)
类和对象(提高) 1、定义一个类 关键字class 6 class Data1 7 { 8 //类中 默认为私有 9 private: 10 int a;//不要给类中成员 初始化 11 protected://保护 12 int b; 13 public://公共 14 int c; 15 //在类的内部 不存在权限之分 16 void showData(void)…...

免费最好用的证件照制作软件,一键换底+老照片修复+图片动漫化,吊打付费!
这款软件真的是阿星用过的,最好用的证件照制作软件,没有之一! 我是阿星,今天要给大家安利一款超实用的证件照工具,一键换底,自动排版,免费无广告,让你在家就能轻松搞定证件照&#…...

antfu/ni 在 Windows 下的安装
问题 全局安装 ni 之后,第一次使用会有这个问题 解决 在 powershell 中输入 Remove-Item Alias:ni -Force -ErrorAction Ignore之后再次运行 ni Windows 11 下的 Powershell 环境配置 可以参考 https://github.com/antfu-collective/ni?tabreadme-ov-file#how …...

Linux 生产消费者模型
💓博主CSDN主页:麻辣韭菜💓 ⏩专栏分类:Linux初窥门径⏪ 🚚代码仓库:Linux代码练习🚚 🌹关注我🫵带你学习更多Linux知识 🔝 前言 1. 生产消费者模型 1.1 什么是生产消…...
深入浅出:MongoDB中的背景创建索引
深入浅出:MongoDB中的背景创建索引 想象一下,你正忙于将成千上万的数据塞入你的MongoDB数据库中,你的用户期待着实时的响应速度。此时,你突然想到:“嘿,我应该给这些查询加个索引!” 没错&…...
Spring事务十种失效场景
首先我们要明白什么是事务?它的作用是什么?它在什么场景下在Spring框架下会失效? 事务:本质上是由数据库和程序之间交互的过程中的衍生物,它是一种控制数据的行为规则。有几个特性 1、原子性:执行单元内,要…...

JELR-630HS漏电继电器 30-500mA 导轨安装 约瑟JOSEF
JELR-HS系列 漏电继电器型号: JELR-15HS漏电继电器;JELR-25HS漏电继电器; JELR-32HS漏电继电器;JELR-63HS漏电继电器; JELR-100HS漏电继电器;JELR-120HS漏电继电器; JELR-160HS漏电继电器&a…...
如何实现一个简单的链表或栈结构
实现一个简单的链表或栈结构是面向对象编程中的基础任务。下面我将分别给出链表和栈的简单实现。 链表(单链表)的实现 链表是由一系列节点组成的集合,每个节点都包含数据部分和指向列表中下一个节点的链接(指针或引用࿰…...

抖音外卖服务商入驻流程及费用分别是什么?入驻官方平台的难度大吗?
随着抖音关于新增《【到家外卖】内容服务商开放准入公告》的意见征集通知(以下简称“通知”)的发布,抖音外卖服务商入驻流程及费用逐渐成为众多创业者所关注和热议的话题。不过,就当前的讨论情况来看,这个话题似乎没有…...

“小红书、B站崩了”,背后的阿里云怎么了?
导语:阿里云不能承受之重 文 | 魏强 7月2日,“小红书崩了”、“B站崩了”等话题登上了热搜。 据第一财经、财联社等报道,7月2日,用户在B站App无法使用浏览历史关注等内容,消息界面、更新界面、客服界面均不可用&…...

nginx的配置文件
nginx.conf 1、全局模块 worker_processes 1; 工作进程数,设置成服务器内核数的2倍(一般不超过8个,超过8个反正会降低性能,4个 1-2个 ) 处理进程的过程必然涉及配置文件和展示页面,也就是涉及打开文件的…...
艾滋病隐球菌病的病原学诊断方法包括?
艾滋病隐球菌病的病原学诊断方法包括()查看答案 A.培养B.隐球菌抗原C.墨汁染色D.PCR 在感染性疾病研究中,单细胞转录组学的应用包括哪些()? A.细胞异质性研究B.基因组突变检测C.感染过程单细胞分析D.代谢通路分析 开展病原微生物网络实验室体系建设,应通…...
jQuery Tooltip 插件使用教程
jQuery Tooltip 插件使用教程 引言 jQuery Tooltip 插件是 jQuery UI 套件的一部分,它为网页元素添加了交互式的提示框功能。通过这个插件,开发者可以轻松地为链接、按钮、图片等元素添加自定义的提示信息,从而增强用户的交互体验。本文将详细介绍如何使用 jQuery Tooltip…...
访问者模式在金融业务中的应用及其框架实现
引言 访问者模式(Visitor Pattern)是一种行为设计模式,它允许你在不改变对象结构的前提下定义作用于这些对象的新操作。通过使用访问者模式,可以将相关操作分离到访问者中,从而提高系统的灵活性和可维护性。在金融业务…...
.npy格式图像如何进行深度学习模型训练处理,亲测可行
import torchimport torch.nn as nnimport torch.nn.functional as Fimport numpy as npfrom torch.utils.data import DataLoader, Datasetfrom torchvision import transformsfrom PIL import Imageimport json# 加载训练集和测试集数据train_images np.load(../dataset/tra…...
XFeat快速图像特征匹配算法
XFeat(Accelerated Features)是一种新颖的卷积神经网络(CNN)架构,专为快速和鲁棒的像匹配而设计。它特别适用于资源受限的设备,同时提供了与现有深度学习方法相比的高速度和准确性。 轻量级CNN架构…...

普元EOS学习笔记-低开实现图书的增删改查
前言 在前一篇《普元EOS学习笔记-创建精简应用》中,我已经创建了EOS精简应用。 我之前说过,EOS精简应用就是自己创建的EOS精简版,该项目中,开发者可以进行低代码开发,也可以进行高代码开发。 本文我就记录一下自己在…...

动态住宅代理IP详细解析
在大数据时代的背景下,代理IP成为了很多企业顺利开展的重要工具。代理IP地址可以分为住宅代理IP地址和数据中心代理IP地址。选择住宅代理IP的好处是可以实现真正的高匿名性,而使用数据中心代理IP可能会暴露自己使用代理的情况。 住宅代理IP是指互联网服务…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...