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、初始化BeanFactory
			ConfigurableListableBeanFactory 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、实例化:非兰加载Bean
				finishBeanFactoryInitialization(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
	 *
	 */
	@Override
	protected void initPropertySources() {
		System.out.println("自定义 initPropertySources");
		getEnvironment().getSystemProperties().put("systemOS", "mac");
		getEnvironment().setRequiredProperties("systemOS");
	}
	/**
	 *
	 * Spring refresh 方法第扩展点加一:refresh方法中的postProcessBeanFactory();
	 * 重写该方法,修改基础容器BeanFactory内容
	 * @param beanFactory
	 *
	 */
	@Override
	protected 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 {


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

整体小结

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每周题解:最大半连通子图

题目链接 最大半连通子图 题目描述 一个有向图 G ( V , E ) G\left(V,E\right) G(V,E) 称为半连通的 (Semi-Connected)&#xff0c;如果满足&#xff1a; ∀ u , v ∈ V \forall u,v\in V ∀u,v∈V&#xff0c;满足 u → v u\to v u→v 或 v → u v\to u v→u&#xff0…

Go语言实现钉钉机器人接入Dify工作流

go语言实现实现钉钉机器人接入dify工作流&#xff0c;完成ai 流式问答 代码地址 有用的话点个star github地址 效果 配置使用 修改.env_template文件 为.env 设置.env文件内的环境变量 API_KEY: dify的api_keyAPI_URL: dify 的api接口CLIENT_ID : 钉钉机器人应用的idCLIENT…

基于Java的家政预约系统设计与实现

作者介绍&#xff1a;计算机专业研究生&#xff0c;现企业打工人&#xff0c;从事Java全栈开发 主要内容&#xff1a;技术学习笔记、Java实战项目、项目问题解决记录、AI、简历模板、简历指导、技术交流、论文交流&#xff08;SCI论文两篇&#xff09; 上点关注下点赞 生活越过…

Docker-compose 实现Prometheus+Grafana监控MySQL及Linux主机

. ├── Grafana │ ├── data │ └── docker-compose.yaml ├── Mysql │ ├── conf │ ├── data │ ├── docker-compose.yaml │ └── logs ├── Mysqld_exporter │ ├── conf │ └── docker-compose.yaml ├── node-exporter │…

RPA 第一课

RPA 是 Robotic Process Automation 的简称&#xff0c;意思是「机器人流程自动化」。 顾名思义&#xff0c;它是一种以机器人&#xff08;软件&#xff09;来替代人&#xff0c;实现重复工作自动化的工具。 首先要说一句&#xff0c;RPA 不是 ChatGPT 出来之后的产物&#x…

推荐三款常用接口测试工具!

接口测试是软件开发中至关重要的一环&#xff0c;通过对应用程序接口进行测试&#xff0c;可以验证其功能、性能和稳定性。随着互联网和移动应用的快速发展&#xff0c;接口测试变得越来越重要。为了提高测试效率和质量&#xff0c;开发人员和测试人员需要使用专业的接口测试工…

自然语言处理学习(2)基本知识 文本预处理+文本数据分析+文本增强

conda activate DL conda deactivate课程链接 一 一些包的安装 1 stanfordcorenlp 在anoconda prompt 里面&#xff1a;进入自己的conda环境&#xff0c;pip install stanfordcorenlp 进入方式 相关包下载&#xff0c;Jar包我没有下载下来&#xff0c;太慢了&#xff0c;这个…

提高Python爬虫的匿名性:代理ip的配置策略

在数字化时代的今天&#xff0c;网络数据采集已成为获取信息的重要手段&#xff0c;尤其在竞争激烈的商业环境中。Python作为一种强大的编程语言&#xff0c;广泛应用于开发各种数据爬虫来自动化地抓取网络信息。然而&#xff0c;随着网站安全意识的提高&#xff0c;越来越多的…

牛客小白月赛97

A.三角形 判断等边三角形&#xff0c;题不难&#xff0c;代码如下&#xff1a; #include <iostream>using namespace std;int a[110];int main() {int n;cin >> n;int x;int mx 0;for(int i 1; i < n; i){cin >> x;mx max(mx, x);a[x];}for(int i 1…

Java OnVif应用PTZ控制

研究OnVif在Java程序中应用&#xff0c;在此作记录&#xff0c;onvif-java-lib/release at master milg0/onvif-java-lib GitHub&#xff0c;在此连接中下载jar&#xff0c;并在项目中引用&#xff0c;该jar封装很好&#xff0c;可以方便快速完成功能 1.登录OnVif 2.PTZ控制…

【大数据】—美国交通事故分析(2016 年 2 月至 2020 年 12 月)

引言 在当今快速发展的数字时代&#xff0c;大数据已成为我们理解世界、做出决策的重要工具。特别是在交通安全领域&#xff0c;大数据分析能够揭示事故模式、识别风险因素&#xff0c;并帮助制定预防措施&#xff0c;从而挽救生命。本文将深入探讨2016年2月至2020年12月期间&…

反射(通俗易懂)

一、反射(Reflection) 反射就是:加载类&#xff0c;并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等) 动态语言&#xff0c;是一类在运行时可以改变其结构的语言&#xff1a;例如新的函数、对象、甚至代码可以被引进&#xff0c;已有的函数可以被删除或是其他…

强化学习的数学原理:值迭代与策略迭代

概述 从课程地图上可以看出来&#xff0c;这是本门课程中第一次正式的介绍强化学习的算法&#xff0c;并且是一个 model-based 的算法&#xff0c;而在下一节课将会介绍第一个 model-free 的算法&#xff08;在 chapter 5&#xff09;。而这两节和之前所学的 BOE 是密切相关的&…

笔记-python爬虫概述

目录 常用第三方库 爬虫框架 动态页面渲染1. url请求分析2. selenium3. phantomjs4. splash5. spynner 爬虫防屏蔽策略1. 修改User-Agent2. 禁止cookies3. 设置请求时间间隔4. 代理IP池5. 使用Selenium6. 破解验证码常用第三方库 对于爬虫初学者&#xff0c;建议在了解爬虫原…

DEX: Scalable Range Indexing on Disaggregated Memory——论文泛读

arXiv Paper 论文阅读笔记整理 问题 内存优化索引[2&#xff0c;3&#xff0c;18&#xff0c;27&#xff0c;42]对于加速OLTP至关重要&#xff0c;但随着数据大小&#xff08;以及索引大小&#xff09;的增长&#xff0c;对内存容量的需求可能会超过单个服务器所能提供的容量…

基于ADRC自抗扰算法的UAV飞行姿态控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 控制系统概述 4.2 ADRC基本框架 4.3 控制律设计 5.完整工程文件 1.课题概述 基于ADRC自抗扰算法的UAV飞行姿态控制系统simulink建模与仿真&#xff0c;分别对YAW&#xff0c;PITCH&#xff0c;ROL…

golang写的自动更新器

文件自动更新器&#xff0c;这个很多端游和软件都有用到的。 golang的rpc通信&#xff0c;是非常好用的一个东西&#xff0c;可以跟调用本地函数一样&#xff0c;调用远程服务端的函数&#xff0c;直接从远程服务端上拉取数据下来&#xff0c;简单便捷。 唯一的遗憾就是&#x…

互联网盲盒小程序的市场发展前景如何?

近几年来&#xff0c;盲盒成为了大众热衷的消费市场。盲盒是一个具有随机性和惊喜感&#xff0c;它能够激发消费者的好奇心&#xff0c;在拆盲盒的过程中给消费者带来巨大的愉悦感&#xff0c;在各种的吸引力下&#xff0c;消费者也愿意为各类盲盒买单。如今&#xff0c;随着盲…

暑假提升(2)[平衡二叉树之一--AVL树]

我不去想未来是平坦还是泥泞&#xff0c;只要热爱生命一切&#xff0c;都在意料之中。——汪国真 AVLTree 1、诞生原因2、什么是AVL树3、如何设计AVL树3、1、AVL树节点的定义3、2、AVL树的插入3、3、平衡因子那些事3、3、1、平衡因子-2/2下的简单情况3、3、2、平衡因子-2/2下的…

tkinter拖入txt文本并显示

tkinter拖入txt文本并显示 效果代码 效果 代码 import tkinter as tk from tkinter import scrolledtext from tkinterdnd2 import DND_FILES, TkinterDnDdef drop(event):file_path event.data.strip({})if file_path.endswith(.txt):with open(file_path, r, encodingutf-8…