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成功的重要原因之一。

整体小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/39997.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty 粘包/拆包、解码工具类

1. 概述 1.1 粘包 发送 abc def&#xff0c;接收 abcdef 原因 滑动窗口&#xff1a;假设发送方 256 bytes 表示一个完整报文&#xff0c;但由于接收方处理不及时且窗口大小足够大&#xff0c;这 256 bytes 字节就会缓冲在接收方的滑动窗口中&#xff0c;当滑动窗口中缓冲了…

模拟 ADC 的前端

ADC 的 SPICE 模拟 反复试验的方法将信号发送到 ADC 非常耗时&#xff0c;而且可能有效也可能无效。如果转换器捕获电压信息的关键时刻模拟输入引脚不稳定&#xff0c;则无法获得正确的输出数据。SPICE 模型允许您执行的步是验证所有模拟输入是否稳定&#xff0c;以便没有错误…

尝试修改苍穹外卖为”李小罗餐厅“

学习苍穹外卖后&#xff0c;将其修改为自己所需要的项目&#xff0c;也是对苍穹外卖项目的加深理解 对项目之间的连接等关系进一步清晰&#xff0c;那么便开始吧 d1_开始修改 修改名字为”李小罗餐厅“ src\views\login\index.vue src\router.ts 结果展示 修改进来之后的展示…

上海站圆满结束!MongoDB Developer Day深圳站,周六见!

在过去两个周六的北京和上海 我们见证了两站热情高涨的 MongoDB Developer Day&#xff01; 近200位参会开发者相聚专业盛会 经过全天的动手实操和主题研讨会 MongoDB技能已是Next Level&#xff01; 最后一站Developer Day即将启程 期待本周六与各位在深圳相见&#xff0…

【Docker安装】OpenEuler系统下部署Docker环境

【Docker安装】OpenEuler系统下部署Docker环境 前言一、本次实践介绍1.1 本次实践规划1.2 本次实践简介二、检查本地环境2.1 检查操作系统版本2.2 检查内核版本2.3 检查yum仓库三、卸载Docker四、部署Docker环境4.1 配置yum仓库4.2 检查可用yum仓库4.3 安装Docker4.4 检查Docke…

Python题解Leetcode Hot100之矩阵

1. 矩阵置零 题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 解题思路 题目要求进行原地更改&#xff0c;也就是不能使用额外的空间&#xff0c;因此我们可以使用第一行的元素来记录对应的…

【LeetCode】十二、递归:斐波那契 + 反转链表

文章目录 1、递归2、leetcode509&#xff1a;斐波那契数列3、leetcode206&#xff1a;反转链表4、leetcode344&#xff1a;反转字符串 1、递归 函数自己调用自己 递归的4个点&#xff1a; 递归的例子&#xff1a;给一个数n&#xff0c;在斐波那契数列中&#xff0c;找到n对应的…

科研与英文学术论文写作指南——于静老师课程

看到了一个特别棒的科研与英文学术论文写作指南&#xff0c;理论框架实例。主讲人是中科院信息工程研究所的于静老师。推荐理由&#xff1a;写论文和读论文或者讲论文是完全不一样的&#xff0c;即使现在还没有发过论文&#xff0c;但是通过于老师的课程&#xff0c;会给后续再…

LSTM水质预测模型实践

0 引言 随着水质自动站的普及&#xff0c;监测频次越来越高&#xff0c;自动监测越来越准确。 水质站点增多&#xff0c;连续的水质监测数据&#xff0c;给水质预测提供更多的训练基础。 长短时记忆网络(LSTM)适用于多变量、连续、自相关的数据预测。 人工神经网络模型特点为的…

使用requests爬取拉勾网python职位数据

爬虫目的 本文是想通过爬取拉勾网Python相关岗位数据&#xff0c;简单梳理Requests和xpath的使用方法。 代码部分并没有做封装&#xff0c;数据请求也比较简单&#xff0c;所以该项目只是为了熟悉requests爬虫的基本原理&#xff0c;无法用于稳定的爬虫项目。 爬虫工具 这次…

LVS 负载均衡群集

一&#xff1a;LVS群集应用基础 1.1&#xff1a;概述 1.群集的类型 无论是哪种群集&#xff0c; 都至少包括两台节点服务器&#xff0c; 而对外表现为一个整体&#xff0c; 只提供一个访问入口。根据群集所针对的目标差异&#xff0c; 可分为以下三种类型。 负载均衡群集&a…

使用U盘重装系统

目录 一、 制作启动盘 1. 准备一个U盘和一台电脑 2. 下载win10安装包 二、安装操作系统 1. 插入系统安装盘 2. 通过进入BIOS界面进入到我们自己制作的启动盘上 三、安装成功后进行常规设置 一、 制作启动盘 1. 准备一个U盘和一台电脑 注意&#xff1a;提前备份好U盘内的…

JDK1.8下载、安装与配置完整图文2024最新教程

一、报错 运行Pycharm时&#xff0c;报错No JVM installation found. Please install a JDK.If you already have a JDK installed, define a JAVA_HOME variable in Computer >System Properties > System Settings > Environment Variables. 首先可以检查是否已安装…

【C语言】qsort()函数详解:能给万物排序的神奇函数

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.qsort()函数的基本信息及功能 二.常见的排序算法及冒泡排序 三.逐一解读qsort()函数的参数及其原理 1.void* base 2.size_t num 3.size_t size 4.int (*compar)(c…

节水增效,蜂窝物联智能灌溉助力农业升级!

智能灌溉的优势主要体现在以下几个方面&#xff1a; 1. 提高效率&#xff1a;智能灌溉可以根据作物生长的不同阶段和环境条件自动调整灌溉时间和水量&#xff0c;减少人工干预的频率和时间&#xff0c;提高了灌溉效率。 2. 节约水资源&#xff1a;智能灌溉可以根据土壤湿度和…

Python爬虫实战案例——王者荣耀皮肤抓取

大家好&#xff0c;我是你们的老朋友——南枫&#xff0c;今天我们一起来学习一下该如何抓取大家经常玩的游戏——王者荣耀里面的所有英雄的皮肤。 老规矩&#xff0c;直接上代码&#xff1a; 导入我们需要使用到的&#xff0c;也是唯一用到的库&#xff1a; 我们要抓取皮肤其…

大陆ARS548使用记录

一、Windows连接上位机 雷达是在深圳路达买的&#xff0c;商家给的资料中首先让配置网口&#xff0c;但我在使用过程中一直出现无法连接上位机的情况。接下来说说我的见解和理解。 1.1遇到的问题 按要求配置好端口后上位机无连接不到雷达&#xff0c;但wireshark可以正常抓到数…

PyPDF2拆分PDF文件的高级应用:指定拆分方式

本文目录 前言一、拆分方式选择1、代码讲解2、实现效果图3、完整代码前言 前两篇文章,分别讲解了将使用PyPDF2将PDF文档分割成为单个页面、在分割PDF文档时指定只分割出指定页面,如果你还没有看过,然后有需要的话,可以去看一下,我把文章链接贴到这里: PyPDF2拆分PDF文件…

Nuxt3 的生命周期和钩子函数(九)

title: Nuxt3 的生命周期和钩子函数&#xff08;九&#xff09; date: 2024/7/3 updated: 2024/7/3 author: cmdragon excerpt: 摘要&#xff1a;本文介绍了Nuxt3中与Vite相关的五个生命周期钩子&#xff0c;包括vite:extend、vite:extendConfig、vite:configResolved、vite…

CVE-2024-6387漏洞预警:尽快升级OpenSSH

OpenSSH维护者发布了安全更新&#xff0c;其中包含一个严重的安全漏洞&#xff0c;该漏洞可能导致在基于glibc的Linux系统中使用root权限执行未经身份验证的远程代码。该漏洞的代号为regreSSHion&#xff0c;CVE标识符为CVE-2024-6387。它驻留在OpenSSH服务器组件&#xff08;也…