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.pswp.cn/diannao/39997.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39997.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39997.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Netty 粘包/拆包、解碼工具類

1. 概述 1.1 粘包 發送 abc def&#xff0c;接收 abcdef 原因 滑動窗口&#xff1a;假設發送方 256 bytes 表示一個完整報文&#xff0c;但由于接收方處理不及時且窗口大小足夠大&#xff0c;這 256 bytes 字節就會緩沖在接收方的滑動窗口中&#xff0c;當滑動窗口中緩沖了…

如何使用Spring Boot實現WebSocket通信

如何使用Spring Boot實現WebSocket通信 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討如何利用Spring Boot實現WebSocket通信&#xff0c;實現實…

第一周java。2

方法的作用 將重復的代碼包裝起來&#xff0c;寫成方法&#xff0c;提高代碼的復用性。 方法的語法 方法的語法格式如下 : [修飾符] 方法返回值類型 方法名(形參列表) { //由零條到多條可執行性語句組成的方法體return 返回值; } 定義方法語法格式的詳細說明如下&#xf…

模擬 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…

js樹形結構遞歸 常用函數

假設有如下樹結構 const treeData [{title: 1,id: 1,key: 1,children: [{title: 1-1,id: 2,key: 2,children: [{title: 1-1-1,id: 3,key: 3,},],},],},{title: 2,id: 2,key: 1,children: [{title: 2-1,id: 4,key: 4,children: [{title: 2-1-1,id: 5,key: 5,},],},],},];根據某…

Python題解Leetcode Hot100之矩陣

1. 矩陣置零 題目描述 給定一個 m x n 的矩陣&#xff0c;如果一個元素為 0 &#xff0c;則將其所在行和列的所有元素都設為 0 。請使用 原地 算法。 解題思路 題目要求進行原地更改&#xff0c;也就是不能使用額外的空間&#xff0c;因此我們可以使用第一行的元素來記錄對應的…

Cesium常見設置視角所用到函數

1.左鍵拾取經緯度坐標 const handler new Cesium.ScreenSpaceEventHandler(viewer.canvas)// 監聽鼠標點擊事件handler.setInputAction(function (click) {// 使用pick函數獲取點擊位置的實際位置var cartesian viewer.scene.pickPosition(click.position);if (Cesium.defin…

【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盤內的…

jQuery Tooltip 插件使用教程

jQuery Tooltip 插件使用教程 引言 jQuery Tooltip 插件是 jQuery UI 套件的一部分,它為網頁元素添加了交互式的提示框功能。通過這個插件,開發者可以輕松地為鏈接、按鈕、圖片等元素添加自定義的提示信息,從而增強用戶的交互體驗。本文將詳細介紹如何使用 jQuery Tooltip…

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…

2024西安國際儲能產業博覽會將于12月5日開幕!

2024西部國際儲能產業博覽會 同期舉辦&#xff1a;2024西部國際氫能源及燃料電池產業博覽會 2024年12月5-7日 西安國際會展中心 規劃展會規模&#xff1a; 50,000 ㎡ 450 60000人次 20場 展區面積 預邀展商 專業觀眾 行業…