others - 如何用Java设置环境变量 ?

如何用Java设置环境变量?

有一个 System.getenv(String) 可用于获取单个环境变量。

是否可以在当前运行的进程中设置环境变量?

时间:


void setUpEnvironment(ProcessBuilder builder) {
 Map<String, String> env = builder.environment();
//blah blah
}

对于需要为单元测试设置特定环境值的场景,你发现以下技巧很有用。 它将改变整个jvm( 在测试之后确保你重置了所有更改) 环境变量, 但不会改变你的系统环境。


protected static void setEnv(Map<String, String> newenv)
{
 try
 {
 Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
 Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
 theEnvironmentField.setAccessible(true);
 Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
 env.putAll(newenv);
 Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
 theCaseInsensitiveEnvironmentField.setAccessible(true);
 Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
 cienv.putAll(newenv);
 }
 catch (NoSuchFieldException e)
 {
 try {
 Class[] classes = Collections.class.getDeclaredClasses();
 Map<String, String> env = System.getenv();
 for(Class cl : classes) {
 if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
 Field field = cl.getDeclaredField("m");
 field.setAccessible(true);
 Object obj = field.get(env);
 Map<String, String> map = (Map<String, String>) obj;
 map.clear();
 map.putAll(newenv);
 }
 }
 } catch (Exception e2) {
 e2.printStackTrace();
 }
 } catch (Exception e1) {
 e1.printStackTrace();
 } 
}


public static void set(Map<String, String> newenv) throws Exception {
 Class[] classes = Collections.class.getDeclaredClasses();
 Map<String, String> env = System.getenv();
 for(Class cl : classes) {
 if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
 Field field = cl.getDeclaredField("m");
 field.setAccessible(true);
 Object obj = field.get(env);
 Map<String, String> map = (Map<String, String>) obj;
 map.clear();
 map.putAll(newenv);
 }
 }
}

在Android上,该接口通过Libcore.os作为一种隐藏的API公开。


Libcore.os.setenv("VAR","value", bOverwrite);
Libcore.os.getenv("VAR"));

Libcore类以及接口操作系统是公共的。 仅缺少类声明,需要将它显示给链接器。 无需将类添加到应用程序中,如果包含了它,也不会造成伤害。


package libcore.io;

public final class Libcore {
 private Libcore() { }

 public static Os os;
}

package libcore.io;

public interface Os {
 public String getenv(String name);
 public void setenv(String name, String value, boolean overwrite) throws ErrnoException;
}


java.lang.String cannot be cast to java.lang.ProcessEnvironment$Variable

下面是一个Scala版本。 希望将其移植到Java中并不太困难。


def setEnv(newenv: java.util.Map[String, String]): Unit = {
 try {
 val processEnvironmentClass = JavaClass.forName("java.lang.ProcessEnvironment")
 val theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment")
 theEnvironmentField.setAccessible(true)

 val variableClass = JavaClass.forName("java.lang.ProcessEnvironment$Variable")
 val convertToVariable = variableClass.getMethod("valueOf", classOf[java.lang.String])
 convertToVariable.setAccessible(true)

 val valueClass = JavaClass.forName("java.lang.ProcessEnvironment$Value")
 val convertToValue = valueClass.getMethod("valueOf", classOf[java.lang.String])
 convertToValue.setAccessible(true)

 val sampleVariable = convertToVariable.invoke(null,"")
 val sampleValue = convertToValue.invoke(null,"")
 val env = theEnvironmentField.get(null).asInstanceOf[java.util.Map[sampleVariable.type, sampleValue.type]]
 newenv.foreach { case (k, v) => {
 val variable = convertToVariable.invoke(null, k).asInstanceOf[sampleVariable.type]
 val value = convertToValue.invoke(null, v).asInstanceOf[sampleValue.type]
 env.put(variable, value)
 }
 }

 val theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment")
 theCaseInsensitiveEnvironmentField.setAccessible(true)
 val cienv = theCaseInsensitiveEnvironmentField.get(null).asInstanceOf[java.util.Map[String, String]]
 cienv.putAll(newenv);
 }
 catch {
 case e : NoSuchFieldException => {
 try {
 val classes = classOf[java.util.Collections].getDeclaredClasses
 val env = System.getenv()
 classes foreach (cl => {
 if("java.util.Collections$UnmodifiableMap" == cl.getName) {
 val field = cl.getDeclaredField("m")
 field.setAccessible(true)
 val map = field.get(env).asInstanceOf[java.util.Map[String, String]]
//map.clear()//Not sure why this was in the code. It means we need to set all required environment variables.
 map.putAll(newenv)
 }
 })
 } catch {
 case e2: Exception => e2.printStackTrace()
 }
 }
 case e1: Exception => e1.printStackTrace()
 }
}

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);
 }
}

...