源起
最近看log4j2源碼發現PropertiesUtil類被用於獲取配置信息(封裝為PropertySource類),可以用於獲取日志工廠的類名和配置工廠的類名等屬性,log4j2默認為我們提供了以下實現類
PropertiesPropertySource:加載classpath:log4j2.component.properties配置文件,權重0
SystemPropertiesPropertySource:獲取System.getProperties()屬性,權重100
EnvironmentPropertySource:獲取System.getenv()屬性,權重-100
log4j2分別為這三個類指定了權重,並通過TreeSet指定比較器按照權重升序存放加載后的PropertySource配置類,最后會循環配置列表,按照指定的key獲取屬性。這樣相同的key,權重高的配置就會覆蓋權重低的配置。按照優先級看,相同的屬性System.getProperties()>System.getenv(),那么這兩個屬性到底有什么區別,為何要指定getProperties()>getenv()
探究
當程序中需要使用與操作系統相關的變量(文件分隔符、換行符等)時,java提供了System類的靜態方法getProperties()和getenv(),用於獲取系統相關的配置屬性。這兩個方法都能將系統相關的配置屬性以key-value的形式傳遞給java進程
getProperties()
引用jdk中的注釋說明Determines the current system properties.:確定當前系統的屬性。我們的java程序運行在jvm虛擬機之上,並不和操作系統直接交互,所以這里的當前系統指的並不是我們的操作系統,而是jvm虛擬機,實際獲取的是jvm虛擬機的系統屬性。
- 查看jvm虛擬機系統屬性
執行%java_home%/bin/jvisualvm.exe,在本地->VisualVM->系統屬性中可以查看當前jvm虛擬機的系統屬性
- 執行getProperties()方法
public static void main(String[] args) { Properties properties = System.getProperties(); properties.forEach(new BiConsumer<Object, Object>() { @Override public void accept(Object o, Object o2) { System.out.println("property:" + o + "=" + o2); } }); }
二者對比可以發現jvm虛擬機中的系統屬性包含getProperties()的結果,引用jdk中的注釋說明This set of system properties always includes values* for the following keys:,該方法在獲取系統屬性的時候指定了需要的keys列表
- 可用的系統屬性匯總
java.version Java :運行時環境版本 java.vendor Java :運行時環境供應商 java.vendor.url :Java供應商的 URL java.home :Java安裝目錄 java.vm.specification.version: Java虛擬機規范版本 java.vm.specification.vendor :Java虛擬機規范供應商 java.vm.specification.name :Java虛擬機規范名稱 java.vm.version :Java虛擬機實現版本 java.vm.vendor :Java虛擬機實現供應商 java.vm.name :Java虛擬機實現名稱 java.specification.version:Java運行時環境規范版本 java.specification.vendor:Java運行時環境規范供應商 java.specification.name :Java運行時環境規范名稱 java.class.version :Java類格式版本號 java.class.path :Java類路徑 java.library.path :加載庫時搜索的路徑列表 java.io.tmpdir :默認的臨時文件路徑 java.compiler :要使用的 JIT編譯器的名稱 java.ext.dirs :一個或多個擴展目錄的路徑 os.name :操作系統的名稱 os.arch :操作系統的架構 os.version :操作系統的版本 file.separator :文件分隔符 path.separator :路徑分隔符 line.separator :行分隔符 user.name :用戶的賬戶名稱 user.home :用戶的主目錄 user.dir:用戶的當前工作目錄
getenv()
引用jdk中的注釋說明Returns an unmodifiable string map view of the current system environment.:獲取不可變的當前系統的環境變量,返回字符串類型的Map。這里的環境包括系統所在環境(操作系統環境變量)和系統自身環境(用戶環境變量)。
不同操作系統設置環境變量方式不同,舉例,windows使用控制面板中系統程序,而Unix使用shell腳本。當創建一個進程時,缺省其繼承其父進程的環境變量副本。
配置jdk的過程就是給操作系統增加環境變量,操作系統環境變量的值可以被所有運行在操作系統上的jvm虛擬機中的java程序獲取
注意:這里返回的Map實際的實現類是UnmodifiableMap,是一個只讀的視圖。
- 可用的環境變量匯總
USERPROFILE :用戶目錄
USERDNSDOMAIN :用戶域
PATHEXT :可執行后綴
JAVA_HOME :Java安裝目錄
TEMP :用戶臨時文件目錄
SystemDrive :系統盤符
ProgramFiles :默認程序目錄
USERDOMAIN :帳戶的域的名稱
ALLUSERSPROFILE :用戶公共目錄
SESSIONNAME :Session名稱
TMP :臨時目錄
Path :path環境變量
CLASSPATH :classpath環境變量
PROCESSOR_ARCHITECTURE :處理器體系結構
OS :操作系統類型
PROCESSOR_LEVEL :處理級別
COMPUTERNAME :計算機名
Windir :系統安裝目錄
SystemRoot :系統啟動目錄
USERNAME :用戶名
ComSpec :命令行解釋器可執行程序的准確路徑
APPDATA :應用程序數據目錄
應用
我們可以通過以下方式設置環境變量和系統屬性
- 環境變量
環境變量沒有提供set方法,程序運行狀態無法添加或修改,只能預先設置(設置方法參考jdk)
idea中可以通過Edit Configurations…->Environment variables:log_dir=/tmp/log進行設置
、
- 系統屬性
運行時更新系統屬性, 可使用System.setProperty()方法:
System.setProperty("log_dir","/tmp/log");
啟動時可以通過命令行方式傳參方式將屬性配置傳遞給應用程序
java -jar xxx.jar -Dlog_dir="/tmp/log"
idea中可以通過Edit Configurations…->VM options:-Dlog_dir=/tmp/log進行設置
總結
- 環境變量是操作系統環境變量的不可變副本,只能預先設置,程序啟動后不可修改
- 作用范圍不同,環境變量是是操作系統級的,對運行在操作系統上的所有應用都有效;系統屬性只對當前jvm虛擬機有效
系統屬性與環境變量都是名稱與值之間的映射。兩種機制都可以將用戶定義的信息傳遞給java進程。
環境變量產生的更多的是全局效應,他們不僅對java子進程可見,而且對於定義它們的所有子進程都是可見的。
程序中盡可能使用系統屬性,而環境變量應該在全局范圍需要時才使用
回首掏
回到最初的問題,log4j2為何要指定getProperties()>getenv()?原因大概就是環境變量的作用范圍要大於系統屬性,為了實現個性化配置覆蓋通用配置吧。
目前主流的框架都會使用環境變量和系統屬性進行配置管理,理解他們對后續框架的學習至關重要。
轉載:https://blog.csdn.net/dcr782195101/article/details/122000536