springboot2配置文件定義${user.name}內容失效問題探究

来源:https://www.cnblogs.com/linyb-geek/archive/2020/07/24/13373627.html
-Advertisement-
Play Games

前言 在朋友的項目有個自定義配置文件user.yml,其內容如下 user: userId: 1 name: 張三 email: [email protected] 其映射實體內容為如下 @Data @AllArgsConstructor @NoArgsConstructor @Builder @Pro ...


前言

在朋友的項目有個自定義配置文件user.yml,其內容如下

user:
  userId: 1
  name: 張三
  email: [email protected]

其映射實體內容為如下

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@PropertySource(value = "user.yml",encoding = "utf-8",factory = CustomYmlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "user")
@Configuration
public class User {

    private String name;

    private Long userId;

    private String email;
}

項目啟動後,輸出的user內容為

User(name=Administrator, userId=1, [email protected])

很明顯name的內容不是我們想要的

排查

源碼.png
從跟蹤的源碼可以發現有個systemProperties配置排在user.yml前面。systemProperties這是個啥東西,見名之意,這明顯就是系統屬性配置。而systemProperties裡面又有啥內容,我們繼續跟蹤下
源碼2.png
源碼3.png
從源碼可以看出systemProperties裡面有個key為user.name,value為Administrator。

從這邊我們可以看出我們控制台列印出來的內容其實是systemProperties的內容。由此我們可以推斷出當系統變數和自定義配置變數都有一樣的key時,將以系統變數的值為準。

看到這邊也許有朋友說你這是在胡說八道,就憑這個現象就得出這個結論。那好為了證明這個結論,我們不得繼續斷點排查下去。不過在斷點之前,我們去spring官網溜溜,看有沒有啥收穫。進官網我們可以看到有這麼一段話
環境變數優於自定義變數.png
這段話的意思是預設情況下,系統屬性優先於環境變數。 因此,如果在調用env.getProperty(“ my-property”)的過程中在兩個地方都同時設置了my-property屬性,則系統屬性值“ wins”並返回。 請註意,屬性值不會合併,而是會被前面的條目完全覆蓋。

看吧,官網它自己也這麼說

如果我們想要自定義的屬性優於系統屬性,要怎麼做

解法.png
這段也是從官網截圖來的,其意思是整個機制是可配置的。 也許您具有要集成到此搜索中的自定義屬性源。 為此,實現並實例化您自己的PropertySource並將其添加到當前環境的PropertySources集中

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

這個是官方解法。

我這邊在提供2種解法。

  • bean初始化之前,修改屬性文件載入順序
  • 在bean初始化後,變更bean的屬性值

其實現代碼如下

ntBeanFactoryPostProcesser implements BeanFactoryPostProcessor, ApplicationContextAware, BeanPostProcessor {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //方法三:在bean初始化後,變更bean的屬性值
//        if("user".equals(beanName)){
//            User user = (User)bean;
//            System.out.println("----------------before---------------------");
//            System.out.println("user-->"+user);
//            System.out.println("----------------after---------------------");
//            String propertySourceName = "user.yml";
//            PropertySource propertySource = getPropertySource(propertySourceName);
//            if(!ObjectUtils.isEmpty(propertySource)){
//               user.setName(String.valueOf(propertySource.getProperty("user.name")));
//            }
//            System.out.println("user-->"+user);
//
//        }
        return bean;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        printPropertySourceNames();
       String propertySourceName = "user.yml";
        PropertySource propertySource = getPropertySource(propertySourceName);
        if(!ObjectUtils.isEmpty(propertySource)){
            //方法一 bean初始化之前,修改屬性文件載入順序
            getEnvironment().getPropertySources().remove(propertySourceName);
            getEnvironment().getPropertySources().addFirst(propertySource);
        }

        // 方法二 新增一個PropertySource,並把他的載入順序置為第一位
//        Map<String, Object> propertiesSource = new HashMap<>();
//        propertiesSource.put("user.name", "張三");
//        PropertySource newPropertySource = new MapPropertySource("newPropertySource", propertiesSource);
//        getEnvironment().getPropertySources().addFirst(newPropertySource);





    }

    private PropertySource getPropertySource(String propertySourceName){
        return getEnvironment().getPropertySources().get(propertySourceName);
    }

    private AbstractEnvironment getEnvironment(){
        return (AbstractEnvironment)applicationContext.getEnvironment();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    private void printPropertySourceNames(){
        getEnvironment().getPropertySources().stream().forEach(p-> System.out.println(p.getName()));
    }



}

改完後,我們看下控制台此時輸出的內容為

User(name=張三, userId=1, [email protected])

總結

其實要想自定義文件屬性值不和系統變數的值產生衝突,最快捷的方法,就是讓自定義文件屬性的key和系統變數的key不一樣就好。能少寫代碼就儘量少寫


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

-Advertisement-
Play Games
更多相關文章
  • Python爬蟲開發與項目實戰從基本的爬蟲原理開始講解,通過介紹Pthyon編程語言與HTML基礎知識引領讀者入門,之後根據當前風起雲涌的雲計算、大數據熱潮,重點講述了雲計算的相關內容及其在爬蟲中的應用,進而介紹如何設計自己的爬蟲應用。主要內容分為基礎篇、中級篇、深入篇,基礎篇包括Python編程基 ...
  • 本篇要學習的內容和知識結構概覽 函數的參數及其傳遞方式 1. 函數參數傳遞方式 傳值: 傳變數值: 將實參記憶體中的內容拷貝一份給形參, 兩者是不同的兩塊記憶體 傳地址值: 將實參所對應的記憶體空間的地址值給形參, 形參是一個指針, 指向實參所對應的記憶體空間 傳引用: 形參是對實參的引用, 形參和實參是同 ...
  • K近鄰演算法(KNN,K-NearestNeighbor)是機器學習或數據分析中最基礎、也是最簡單的演算法之一,這個演算法的思路就如同它字面上的意思“K個最近的鄰居”,想要得到某個樣本的某個特征的值(一個樣本通常有多個特征),就需要找到距離它最近的K個樣本,然後根據這些樣本的該特征的近似值作為它的特征值。 ...
  • 百度雲盤:Python入門經典以解決計算問題為導向的Python編程實踐PDF高清完整版免費下載 提取碼:6e8d 內容簡介 《Python入門經典:以解決計算問題為導向的Python編程實踐》是一本系統而科學的Python入門教程,美國密歇根州立大學等多所美國知名高校採用其作為編程語言的入門教材, ...
  • 大量使用的對象,重覆的創建和銷毀,很耗費性能,這個時候就要使用對象池技術。 ...
  • 一、函數嵌套 1.函數的嵌套調用 在調用一個函數的過程中又調用其他函數 將一個大工能拆解成很多小功能 每個函數名都是全局變數,可以在全局有效 2.函數的嵌套定義 在函數內定義其他函數 子函數只能能在函數中被使用,子函數名只在局部有效 最外層函數相當於一個容器,裝了很多子函數 3.函數的嵌套調用和嵌套 ...
  • 百度網盤:Python項目開發實戰(第2版)PDF高清完整版免費下載 提取碼:exep 內容簡介 本書來自真正的開發現場,是BePROUD公司眾多極客在真實項目中的經驗總結和智慧結晶。作者從Python的環境搭建開始講起,介紹了Web應用的開發方法、項目管理及審查、測試與高效部署、伺服器調試等內容, ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:彥頁走刀口 今天我們來看看用ghpython怎麼實現koch曲線的分形效果,前兩天分享的雪花分形是利用grasshopper的迴圈插件anemone實現的,然後有個小伙 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...