如何从Java中设置环境变量?我发现我可以使用ProcessBuilder为子流程做到这一点。不过,我有几个子流程要启动,所以我宁愿修改当前流程的环境,让子流程继承它。
有一个System.getenv(String)用于获取单个环境变量。我还可以使用System.getenv()获得完整环境变量集的Map。但是,在该Map上调用put()会抛出UnsupportedOperationException——显然,它们意味着环境是只读的。并且,没有System.setenv()。
那么,有没有办法在当前运行的进程中设置环境变量呢?如果有,怎么做?如果不是,理由是什么?(是不是因为这是Java,所以我不应该做邪恶的、不可移植的、过时的事情,比如触摸我的环境?)如果不是,有什么好的建议来管理我将需要提供给几个子流程的环境变量更改吗?
事实证明,@pushy/@anonymous/@Edward Campbell的解决方案在Android上不起作用,因为Android并不是真正的Java。具体来说,Android根本没有java.lang.ProcessEnvironment。但事实证明在Android中更容易,你只需要对POSIX setenv()进行JNI调用:
打印C / JNI:
JNIEXPORT jint JNICALL Java_com_example_posixtest_Posix_setenv
(JNIEnv* env, jclass clazz, jstring key, jstring value, jboolean overwrite)
{
char* k = (char *) (*env)->GetStringUTFChars(env, key, NULL);
char* v = (char *) (*env)->GetStringUTFChars(env, value, NULL);
int err = setenv(k, v, overwrite);
(*env)->ReleaseStringUTFChars(env, key, k);
(*env)->ReleaseStringUTFChars(env, value, v);
return err;
}
在Java中:
public class Posix {
public static native int setenv(String key, String value, boolean overwrite);
private void runTest() {
Posix.setenv("LD_LIBRARY_PATH", "foo", true);
}
}
蒂姆·瑞安的回答对我很管用……但我希望它用于Groovy(例如Spock上下文),并且simplissimo:
import java.lang.reflect.Field
def getModifiableEnvironmentMap() {
def unmodifiableEnv = System.getenv()
Class cl = unmodifiableEnv.getClass()
Field field = cl.getDeclaredField("m")
field.accessible = true
field.get(unmodifiableEnv)
}
def clearEnvironmentVars( def keys ) {
def savedVals = [:]
keys.each{ key ->
String val = modifiableEnvironmentMap.remove(key)
// thinking about it, I'm not sure why we need this test for null
// but haven't yet done any experiments
if( val != null ) {
savedVals.put( key, val )
}
}
savedVals
}
def setEnvironmentVars(Map varMap) {
modifiableEnvironmentMap.putAll(varMap)
}
// pretend existing Env Var doesn't exist
def PATHVal1 = System.env.PATH
println "PATH val1 |$PATHVal1|"
String[] keys = ["PATH", "key2", "key3"]
def savedVars = clearEnvironmentVars(keys)
def PATHVal2 = System.env.PATH
println "PATH val2 |$PATHVal2|"
// return to reality
setEnvironmentVars(savedVars)
def PATHVal3 = System.env.PATH
println "PATH val3 |$PATHVal3|"
println "System.env |$System.env|"
// pretend a non-existent Env Var exists
setEnvironmentVars( [ 'key4' : 'key4Val' ])
println "key4 val |$System.env.key4|"
Kotlin中的一个版本,在这个算法中,我创建了一个装饰器,允许您从环境中设置和获取变量。
import java.util.Collections
import kotlin.reflect.KProperty
class EnvironmentDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return System.getenv(property.name) ?: "-"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
val key = property.name
val classes: Array<Class<*>> = Collections::class.java.declaredClasses
val env = System.getenv()
val cl = classes.first { "java.util.Collections\$UnmodifiableMap" == it.name }
val field = cl.getDeclaredField("m")
field.isAccessible = true
val obj = field[env]
val map = obj as MutableMap<String, String>
map.putAll(mapOf(key to value))
}
}
class KnownProperties {
var JAVA_HOME: String by EnvironmentDelegate()
var sample: String by EnvironmentDelegate()
}
fun main() {
val knowProps = KnownProperties()
knowProps.sample = "2"
println("Java Home: ${knowProps.JAVA_HOME}")
println("Sample: ${knowProps.sample}")
}
有三个库可以在单元测试期间做到这一点。
有Stefan Birkner的系统规则和系统Lambda - https://www.baeldung.com/java-system-rules-junit,允许你做一些事情:
public class JUnitTest {
@Rule
public EnvironmentVariables environmentVariables = new EnvironmentVariables();
@Test
public void someTest() {
environmentVariables.set("SOME_VARIABLE", "myValue");
// now System.getenv does what you want
}
}
或System-Lambda:
@Test
void execute_code_with_environment_variables(
) throws Exception {
List<String> values = withEnvironmentVariable("first", "first value")
.and("second", "second value")
.execute(() -> asList(
System.getenv("first"),
System.getenv("second")
));
assertEquals(
asList("first value", "second value"),
values
);
}
上述功能也可以通过系统存根作为JUnit 5扩展:
@ExtendWith(SystemStubsExtension.class)
class SomeTest {
@SystemStub
private EnvironmentVariables;
@Test
void theTest() {
environmentVariables.set("SOME_VARIABLE", "myValue");
// now System.getenv does what you want
}
}
系统存根向后兼容System Lambda和System Rules,但支持JUnit 5。
另外,还有JUnit Pioneer - https://github.com/junit-pioneer/junit-pioneer,它允许在测试时通过注释设置环境变量。