Spring擴展點之BeanPostProcessor

来源:https://www.cnblogs.com/zhixiang-org-cn/archive/2019/09/16/11530911.html
-Advertisement-
Play Games

前言 介面是Spring中一個非常重要的介面,它的介面定義如下 當你實現了這個介面的時候,Spring會保證在每一個bean對象初始化方法調用之前調用 方法,在初始化方法調用之後調用 的註冊 看過我之前寫的IOC源碼分析系列文章的同學應該對這個都比較有印象 ) Spring在執行到這的時候會把所有實 ...


前言

BeanPostProcessor介面是Spring中一個非常重要的介面,它的介面定義如下

public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

當你實現了這個介面的時候,Spring會保證在每一個bean對象初始化方法調用之前調用postProcessBeforeInitialization方法,在初始化方法調用之後調用postProcessAfterInitialization

BeanPostProcessor的註冊

看過我之前寫的IOC源碼分析系列文章的同學應該對這個都比較有印象

Spring在執行到這的時候會把所有實現BeanPostProcessor介面的實現類都註冊到BeanFactory中,一起來看一下實現的細節

    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
    }
public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
        //獲取所有BeanPostProcessor的實現類
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);


        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

          // 這裡把實現PriorityOrdered介面,Ordered 介面的BeanPostProcessors 和其他類型的BeanPostProcessors 區分開
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        //對實現了PriorityOrdered介面的按優先順序排序
        sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
       //這裡就是註冊了,下麵會說
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

        
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(beanFactory, orderedPostProcessors);
       //註冊
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        //註冊
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

        // 最後註冊常規的
        sortPostProcessors(beanFactory, internalPostProcessors);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);

        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }

可以看到上方的代碼就是把這些BeanPostProcessor分為了幾類,然後分別根據規則排序後註冊進BeanFactory中,而BeanFactory中其實就只是維護了一個BeanPostProcessor的列表而已

  private final List<BeanPostProcessor> beanPostProcessors = new ArrayList();

private static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {

        for (BeanPostProcessor postProcessor : postProcessors) {
            beanFactory.addBeanPostProcessor(postProcessor);
        }
    }

public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
            this.hasInstantiationAwareBeanPostProcessors = true;
        }

        if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
            this.hasDestructionAwareBeanPostProcessors = true;
        }

    }

執行原理

我們知道Bean的初始化是在定義在容器的刷新過程中,而具體的實現則是由AbstractAutowireCapableBeanFactory.initializeBean()方法完成的。在這個方法中就包含了BeanPostProcessor的調用邏輯

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }
 
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            // BeanPostProcessors 的Before 方法
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }
 
        try {
            // 調用初始化方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
 
        if (mbd == null || !mbd.isSynthetic()) {
            // BeanPostProcessors 的After方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

而這裡面的執行邏輯我們也可以猜到,無非就是迴圈遍歷所有的BeanPostProcessor,然後一一執行


public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;}

其中applyBeanPostProcessorsAfterInitialization的實現內容跟這個是一樣的

但是這裡面有一個主意的點,那就是如果具體的實現一但返回null,那麼就會跳出for迴圈,後面的就得不到機會執行了

常見用例

查看這個介面的繼承體系,可以看到這個介面的實現類是非常多的,各個實現類的功能如果感興趣大家可以去慢慢挖掘一下
2


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用工具:IronPython 工具介紹:是一種在 .NET 及 Mono上的 Python 實現,是一個開源的項目,基於微軟的 DLR 引擎。(個人理解就是在 .net上面運行Python代碼) 使用方法:先新建一個控制台應用程式 => 使用Nuget 添加IronPython包 => 在Main ...
  • 1、元素的分類 需求:有如下集合[11,22,33,44,55,66,77,88,99,90……],將所有大於66的值保存在字典的第一個key中,將小於66的值保存在第二個key的值中 代碼實現: 1 #定義一個list列表 2 li = [11,22,33,44,55,66,77,88,99,90 ...
  • 2019-09-16-23:09:06 自學Python的第六天,也是寫博客的第六天 今天學的內容是有關dict字典的用法 看視頻加上練習,目前還沒遇到有難點,但是感覺很不好的樣子 沒有難點以後突然出現一個有關字典的程式感覺要炸,還是得繼續掌握 看最後的代碼吧,有更好的請告訴我 我 是 一 條 快 ...
  • 一:MonoDB的簡單介紹 MongoDB是一個介於關係型資料庫與非關係型資料庫中間的資料庫,是使用C++進行編寫的,他的優點是在支持的查詢格式特別的強大,可以進行存儲比較複雜的數據類型,支持建立索引 二:下載 官方地址:https://www.mongodb.com/ 本教程下載 3.4版本:ht ...
  • Python分散式爬蟲必學框架Scrapy打造搜索引擎 部分課程截圖: 點擊鏈接或搜索QQ號直接加群獲取其它資料: 鏈接:https://pan.baidu.com/s/1-wHr4dTAxfd51Mj9DxiJ4Q 提取碼:ik1n 免費分享,如若鏈接失效請加群 其它資源在群里,私聊管理員即可免費 ...
  • 又是兩個月的時間過去了,上一次寫博客是7月14號,時間還是過的很快的,那麼問題來了,為什麼這麼長時間都沒有寫東西了呢?難道是在打醬油? 哈哈,說起來很慚愧,剛剛開始工作,碰到各種的問題要去學習要去解決,然後業餘的時間又去學了一些奇奇怪怪的東西,導致博客一直都落下了,歸根到底,還是自己懶惰了,因為心中 ...
  • 面試題 dubbo 的 spi 思想是什麼? 面試官心理分析 繼續深入問唄,前面一些基礎性的東西問完了,確定你應該都 ok,瞭解 dubbo 的一些基本東西,那麼問個稍微難一點點的問題,就是 spi,先問問你 spi 是啥?然後問問你 dubbo 的 spi 是怎麼實現的? 其實就是看看你對 dub ...
  • AOP簡介 今天來介紹一下AOP。AOP,中文常被翻譯為“面向切麵編程”,其作為OOP的擴展,其思想除了在Spring中得到了應用,也是不錯的設計方法。通常情況下,一個軟體系統,除了正常的業務邏輯代碼,往往還有一些功能性的代碼,比如:記錄日誌、數據校驗等等。最原始的辦法就是直接在你的業務邏輯代碼中編 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...