
System.setProperty 的正確姿勢Spring Boot 啟動類里的缺省值魔法大家好我是凱哥Java本文標(biāo)簽springBoot啟動參數(shù)本文已參與久久公益活動期間9月1日到9月30日文章開啟贊賞并發(fā)起“一起捐” 文章每獲得 1 個點贊騰訊公益慈善基金會將向該文章支持的公益項目捐贈 0.1元公益金。讓每一次認可匯成對公益項目的實際支持一起讓好事發(fā)生。上周調(diào)試本地模擬數(shù)據(jù)中臺時IDE 的 Program arguments 配了三回都沒生效。換臺機器、換個啟動方式又得從頭再來一遍。后來我把三個參數(shù)寫進啟動類用一個小方法兜底命令行給了就用命令行的沒給就用代碼里的。從此任何機器、任何啟動方式都能開箱即用刪 mock 包那天把注釋標(biāo)記的這段代碼一起刪掉就行。這篇文章把三件事說清楚System.setProperty 這行代碼到底在干什么、缺省值模式怎么落地、不寫啟動類時 java -jar 命令行怎么配。1. 問題IDE 的 Program arguments 為什么不牢靠(1) 參數(shù)只掛在當(dāng)前的 Run Configuration 上IDEA 里的 Program arguments 不是項目屬性是某個 Run Configuration 的屬性。今天在這個配置里填了明天用 mvn spring-boot:run 啟動、或者打 jar 跑、或者同事的機器上 clone 下來參數(shù)就沒了一切重來。(2) 更隱蔽的-- 參數(shù)進不了 System.getProperty配了沒生效很多時候不是沒填對是讀取方式對不上。Spring Boot 會把 --mock.center.enabledtrue 解析進 Environment 的命令行屬性源但不會寫進 System.getProperty。代碼里如果直接 System.getProperty(mock.center.enabled) 去讀永遠讀不到這個值。填了等于沒填說的就是這種情況。2. 方案啟動類里的缺省值(1) System.setProperty 這行代碼在干什么System.setProperty(key, value) 是往 JVM 的系統(tǒng)屬性表里寫一個鍵值對System.getProperty(key) 再把它讀出來。這個表是進程級的當(dāng)前 JVM 里的代碼都能看到進程結(jié)束就清空。它和啟動參數(shù) -D 是同一件事——java -Dmock.center.enabledtrue 本質(zhì)上是 JVM 啟動時替你把 setProperty 調(diào)好了。Spring 的 Environment 也會把系統(tǒng)屬性當(dāng)成一個屬性來源所以 Value 同樣讀得到。(2) 三行代碼落地public static void main(String[] args) { defaultProp(mock.center.enabled, true); defaultProp(mock.userCount, 2600); defaultProp(steel.baseUrl, http://127.0.0.1:28099); SpringApplication.run(HuaweiCenterApplication.class, args); } private static void defaultProp(String key, String value) { if (System.getProperty(key) null) { System.setProperty(key, value); } }defaultProp 的邏輯就一行判斷System.getProperty(key) 是 null說明沒人配過寫入缺省值不是 null說明命令行已經(jīng)給了跳過。這一行判斷就是整個方案的優(yōu)先級規(guī)則。(3) 為什么放在 main 最前面main 的第一行代碼執(zhí)行時-D 參數(shù)早就被 JVM 寫進系統(tǒng)屬性了。把缺省值放在 SpringApplication.run 之前設(shè)置應(yīng)用上下文啟動時屬性已經(jīng)就位Value、Environment、常量類都能拿到。3. 原理-D、--、代碼缺省值誰說了算(1) -DJVM 參數(shù)main 之前就緒-Dmock.center.enabledfalse 由 JVM 在啟動時寫入系統(tǒng)屬性早于 main。defaultProp 的 null 檢查天然放行屬性已存在不覆蓋。它對 System.getProperty 和 Environment 都可見是最可靠的覆蓋方式。(2) --Program arguments進 Environment不進系統(tǒng)屬性Spring Boot 把 --mock.center.enabledfalse 解析為命令行屬性源優(yōu)先級在系統(tǒng)屬性之上但 System.getProperty 讀不到它。只有走 EnvironmentValue、Environment.getProperty、配置綁定才能讀到。(3) 于是優(yōu)先級取決于代碼怎么讀代碼用 Value 或 Environment 讀-- 能蓋過代碼缺省值注釋里命令行優(yōu)先級高于此處成立。代碼直接 System.getProperty 讀-- 無效只能靠 -D 覆蓋。代碼缺省值永遠兜底前兩者都沒給時用它。4. 不寫啟動類java -jar 命令行怎么配(1) 用 -D最直接不寫啟動類就把參數(shù)放到 JVM 層java -Dmock.center.enabledtrue -Dmock.userCount2600 -jar app.jar注意 -D 必須放在 -jar 前面。-D 是 JVM 參數(shù)-jar 后面的內(nèi)容屬于程序參數(shù) argsSpring Boot 只會把它當(dāng)命令行屬性處理不會寫進系統(tǒng)屬性。(2) 用 -- 也行前提是代碼走 Environment 讀java -jar app.jar --mock.center.enabledtrue 這條等價于在 IDE 里填 Program arguments。代碼用 Value 讀沒問題要是直接 System.getProperty 讀這條命令對你的代碼無效。(3) 優(yōu)先級串起來按 Spring 屬性源順序命令行參數(shù) -- 最高其次是 JVM 系統(tǒng)屬性-D 和代碼 set 的都在這層再到 application.yml。所以即使啟動類里寫了 defaultProp(mock.center.enabled, true)命令行 java -Dmock.center.enabledfalse 依然能把它關(guān)掉。(4) 怎么驗證啟動后打印一行 System.getProperty(mock.center.enabled) 看看或者查 Actuator 的 /env 端點最直觀。5. 實戰(zhàn)2600 個模擬用戶測無 totalSize 的滿頁翻頁(1) 數(shù)據(jù)怎么設(shè)計mock.userCount2600單頁上限 2000 條第一頁 2000 條恰好滿頁、第二頁 600 條不滿頁一次覆蓋滿頁繼續(xù)翻、不滿頁停止兩個分支。用戶清單和日用電量都會超過單頁 2000 條專門用來踩分頁邊界。(2) 為什么這個數(shù)值得測接口不返回 totalSize 時只能靠這一頁取沒取滿 2000 條判斷還有沒有下一頁。這種邏輯最容易在兩類邊界出錯恰好整除 2000以及余數(shù)頁。2600 恰好兩個都占整除點2000和余數(shù)600。6. 什么時候別用這個模式(1) 臨時測試參數(shù)合適一行注釋 三行代碼刪包即刪參團隊無感知。(2) 長期配置交給 profile 和配置中心application-local.yml、spring.profiles.active、Nacos 才是長期配置的入口啟動類里的參數(shù)不該變成正式配置的一部分。(3) 防止誤提交生產(chǎn)給 mock 開關(guān)加環(huán)境判斷或 Profile(local) 限定。模擬中臺一旦誤開進生產(chǎn)后果得自己收拾。這套東西不復(fù)雜真正容易栽跟頭的是誰在什么時候?qū)懥藢傩?、誰在什么時候讀。IDE 配置沒生效、讀取方式對不上這兩種坑我都踩過。你要是也遇到過類似的留言說說你的版本。