是否可以在运行时从Java设置环境变量?

时间:2009-02-24 01:02:49

标签: java environment-variables

是否可以在运行时从Java设置环境变量 应用? 在Java 1.5 java.lang.System类中有getenv()方法,我会的 只需要一个setenv()方法......

是否可以修改java进程本身的环境变量;不在孩子的过程中。

是否可以通过JNI实现?那怎么会有用呢?

感谢。

编辑: 好吧,让我这样说吧 - 我们可以用Java做以下几点。请回答。

  1. 我们可以修改当前流程的环境吗?
  2. 我们可以修改父进程的环境吗?
  3. 我们可以修改子进程的环境吗?
  4. Hemal Pandya回答说:“您可以修改当前和子进程的环境,但不能修改产生此进程的父进程的环境。”你同意这个吗?

6 个答案:

答案 0 :(得分:34)

如果我的直觉是正确的,并且您实际上想要为衍生(分叉)子流程(Runtime.getRuntime().exec())的利益修改环境,那么使用{{3}而不是exec()。您可以通过ProcessBuilder实例的ProcessBuilder方法构建自定义环境。

如果这是你想要实现的目标,那么请不要理会这个答案。


<强>更新

您的三个更新的具体问题的答案如下:

  1. 我们可以修改当前流程的环境吗?
    • 不容易。取决于您是否要更改进程的环境,更改System.getenv()在同一JVM中返回的值,或两者都有。
    • 正如Greg Hewgill指出的那样,要更改当前进程的环境,您可以通过JNI调用setenv或其特定于平台的等效项。您也可以使用下面 point 2 中极其复杂的方法,该方法适用于任何进程(前提是您拥有权限。)但是,请注意,在大多数JVM中,此更改可能永远不会反映在值中由System.getenv()返回,因为环境通常在java.util.Map(或等效的)虚拟机启动时缓存。
    • 要更改JVM的环境缓存副本,当使用缓存时(请参阅System.java中的源代码,无论您将使用哪个JVM分发版),您可以尝试黑客攻击实现(通过类加载顺序,environment()reflection。例如,对于SUN的v1.6 JVM,环境缓存由未记录的ProcessEnvironment类(您可以修补)进行管理。 )
  2. 我们可以修改父进程的环境吗?
  3. 我们可以修改子进程的环境吗?
    • ,在产生流程时通过ProcessBuilder
    • 如果在需要进行环境更改时已经生成了该进程,则需要上面的方法2 (或者某些同样复杂的方法,例如在生成时的代码注入,通过例如插槽进行进一步控制由父进程。)
  4. 请注意,除了涉及ProcessBuilder的方法之外,上述所有方法都是脆弱的,容易出错,不可移植到不同程度,并且在多线程环境中容易出现竞争条件。

答案 1 :(得分:6)

回答您的最新问题:

  1. 我们可以修改当前流程的环境吗? ,如果您使用JNI拨打setenv()或其他内容。您可能不需要这样做,并且它可能无法在所有情况下都有效。
  2. 我们可以修改父进程的环境吗? 即可。
  3. 我们可以修改子进程的环境吗? ,使用ProcessBuilder

答案 2 :(得分:5)

您可以获得ProcessEnvironment所持有的底层地图的句柄,然后添加新内容并删除所有您想要的内容。

这适用于java 1.8.0_144。不能保证它适用于任何其他版本的java,但如果你真的需要在运行时更改环境,它可能是相似的。

private static Map<String,String> getModifiableEnvironment() throws Exception{
    Class pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv");
    getenv.setAccessible(true);
    Object unmodifiableEnvironment = getenv.invoke(null);
    Class map = Class.forName("java.util.Collections$UnmodifiableMap");
    Field m = map.getDeclaredField("m");
    m.setAccessible(true);
    return (Map) m.get(unmodifiableEnvironment);
}

在获得对地图的引用后,只需添加您想要的任何内容,您现在可以使用常规的旧System.getenv(&#34;&#34;)调用来检索它。

我试过这个,它在MAC中工作,在os java版本1.8_161

中都没有在Windows中工作

答案 3 :(得分:2)

@Narcoleptic Snowman的回答使我处在正确的轨道上,但是它神秘地不适用于Oracle JDK 1.8.0_231(他使用_144次要版本进行了测试)。即使我能够更新基础地图(通过在向地图添加新属性之前和之后打印出System.getenv()进行验证),使用System.getenv("property")检索新属性时,更改也不会反映出来。

经过一番调查,我发现这是因为System.getenv()System.getenv("property")最终使用了java.lang.ProcessEnvironment的不同静态属性,这些属性在类的static块中初始化。因此,是否将新属性添加到使用System.getenv()检索的地图中都没有关系;这些属性将在System.getenv("property")使用的其他Map中不可用。

因此,我更改了@Narcoleptic Snowman的答案以处理这种情况,并转到下面的代码。请注意,这仅在使用System.getenv("property")检索属性时才有效;如果您使用System.getenv().get("property"),那么他的答案就是您所需要的。用法如下:

@SuppressWarnings("unchecked")
private static Map<String, String> getModifiableEnvironment() throws Exception
{
    Class<?> pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv", String.class);
    getenv.setAccessible(true);
    Field props = pe.getDeclaredField("theCaseInsensitiveEnvironment");
    props.setAccessible(true);
    return (Map<String, String>) props.get(null);
}

此方法应如下使用:

getModifiableEnvironment().put("propName", "propValue");
System.getenv("propName"); // this will return "propValue"

答案 4 :(得分:1)

我不这么认为,至少不是纯粹用Java,但为什么你需要这样做呢?在Java中,最好通过System.getProperties()使用属性,您可以修改它们。

如果你真的必须,我相信你可以在JNI电话中包装C setenv功能 - 事实上,如果有人已经这样做了,我也不会感到惊讶。不过,我不知道代码的细节。

答案 5 :(得分:0)

您可以修改当前和子进程的环境,但不能修改生成此进程的父进程的环境。