java环境变量(env)和系统属性(property)
源起
最近看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