Java单元测试动态修改环境变量
Java单元测试动态修改环境变量
今天再写单元测试的时候,遇到⼀个⽐较有趣的事情,程序需要读取⼀个环境变量,⽽这个变量⼜是动态⽣成的,所以在执⾏单元测试之前要进⾏环境变量的配置。⽬前总结了两种⽅案可以实现动态修改环境变量,修改后的环境变量仅对当前进程⽣效,即仅在当前Java进程中调⽤v(name)⽣效,分别是通过反射修改Runtime中保存环境变量的Map、和通过JNI的⽅式调⽤系统的setenv⽅法。下⾯将分别对两种⽅案进⾏实现。
1 通过反射修改Runtime中保存环境变量的Map
在Java中获取环境变量,可以使⽤如下两种⽅法:
public static String getenv(String name);
java设置环境变量的方法代码
public static java.util.Map<String,String>getenv();
getenv()返回的是⼀个Map,但是这个map是不可修改的,如果我们对其修改会报如下错误:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableMap.put(Collections.java:1459)
amples.jni.EnvNativeUtilsTest.main(EnvNativeUtilsTest.java:8)
这种⽅案修改环境变量的原理很简单,我们可以通过反射的⽅式拿到这个Map,并对其修改即可。直接上代码:
amples.jni;
import flect.Field;
import flect.Method;
import java.util.Map;
/**
* @author shirukai
*/
public class EnvUtils {
public static void setEnv(String name, String value)throws Exception {
getModifiableEnvironment().put(name, value);
}
/**
* 通过反射的⽅式从Runtime中获取存储环境变量的Map,返回的Map是可变的
* Copy from stackoverflow/questions/580085/is-it-possible-to-set-an-environment-variable-at-runtime-from-java      *
* @return Map<String, String></String,String>
* @throws Exception e
*/
@SuppressWarnings("unchecked")
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 = DeclaredField("m");
m.setAccessible(true);
return(Map<String, String>) m.get(unmodifiableEnvironment);
}
public static void main(String[] args)throws Exception {
EnvUtils.setEnv("TEST_SET_ENV","test-set-env");
System.out.v("TEST_SET_ENV"));
}
}
2 通过JNI的⽅式调⽤系统的setenv⽅法
2.1 基本概念
2.2 setenv⽅法
有了JNI,我们就可以很⽅便的调⽤标准库中的setenv⽅法,⾸先我们可以在Linux的环境中,输⼊man setenv
从说明⽂档可以看出,要使⽤setenv⽅法⾸先要引⼊标准库,然后调⽤该⽅法,当前⽅法参数也⾮常简单name为变量名称,value为变量值,overwrite为是否覆盖的标志位,如果为0不覆盖,⾮零将进⾏覆盖。
2.3 使⽤JNI调⽤setenv⽅法
JNI开发的基本流程如下():
1、编写声明了native⽅法的Java类
2、将Java源代码编译成class字节码⽂件
3、⽤javah -jni命令⽣成.h头⽂件(javah是jdk⾃带的⼀个命令,-jni参数表⽰将class中⽤native声明的函数⽣成jni规则的函数)
4、⽤本地代码实现.h头⽂件中的函数
5、将本地代码编译成动态库(windows:.dll,linux/unix:.so,mac os x:*.jnilib)
6、拷贝动态库⾄ java.library.path 本地库搜索⽬录下,并运⾏Java程序
下⾯就将⼀步⼀步实现,笔者电脑为Mac,仅记录在Mac下的实现,其它平台实现参考具体资料。
2.3.1 编写声明了native⽅法的Java类
⾸先我们创建⼀个名为EnvNativeUtils的类:
public class EnvNativeUtils {
public static native void setEnv(String name, String value);
}
2.3.2 编译
编译很简单,我们可以直接使⽤Maven的compile直接编译,这时会在target/classes下⽣成对应的class⽂件,这⾥不做演⽰。
2.3.3 ⽤javah -jni命令⽣成.h头⽂件
使⽤如下命令⽣成.h头⽂件:
javah -classpath /Users/shirukai/hollysys/repository/learn-demo-java/examples/target/classes/ amples.jni.EnvNativeUtils
-classpath 指定编译后的类路径,这⾥笔者指定的是绝对路径,后⾯的参数是包路径。
执⾏命令后,会在当前⽂件夹下⽣成⼀个名为learn_examples_jni_EnvNativeUtils.h的头⽂件,内容如下:
/
* DO NOT EDIT THIS FILE - it is machine generated */
#include<jni.h>
/* Header for class learn_examples_jni_EnvNativeUtils */
#ifndef _Included_learn_examples_jni_EnvNativeUtils
#define _Included_learn_examples_jni_EnvNativeUtils
#ifdef __cplusplus
extern"C"{
#endif
/*
* Class:    learn_examples_jni_EnvNativeUtils
* Method:    setEnv
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_learn_examples_jni_EnvNativeUtils_setEnv
(JNIEnv *, jclass, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
2.3.4 实现.h头⽂件中的函数
在当前⽂件夹下新建⼀个名为EnvNativeUtils.c的⽂件,内容如下:
#include"learn_examples_jni_EnvNativeUtils.h"
#include<stdlib.h>
#ifdef __cplusplus
extern"C"
{
#endif
/*
* Class:    com_study_jnilearn_HelloWorld
* Method:    sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT void JNICALL Java_learn_examples_jni_EnvNativeUtils_setEnv
(JNIEnv *env, jclass cls, jstring j_name, jstring j_value)
{
const char*name =NULL;
const char*value =NULL;
char buff[128]={0};
name =(*env)->GetStringUTFChars(env, j_name,NULL);
value =(*env)->GetStringUTFChars(env, j_value,NULL);
setenv(name,value,0);
}
#ifdef __cplusplus
}
#endif
2.3.5 将本地代码编译成动态库
使⽤如下命令编译成动态库
gcc -dynamiclib -o /Users/shirukai/hollysys/repository/learn-demo-java/examples/src/main/java/learn/examples/jni/libenvnativeutils.jnilib /Users/shirukai/holl ysys/repository/learn-demo-java/examples/src/main/java/learn/examples/jni/EnvNativeUtils.c -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin
2.3.6 代码中使⽤绝对路径加载动态库
执⾏上⾯命令之后,会在对应的⽬录⽣成⼀个libenvnativeutils.jnilib的动态库,我们在代码⾥可以直接指定动态库的路径对其进⾏加载
public class EnvNativeUtils {
public static native void setEnv(String name, String value);
static{
System.load("/Users/shirukai/hollysys/repository/learn-demo-java/examples/src/main/java/learn/examples/jni/libenvnativeutils.jnilib"); }
}
2.3.7 验证
上述操作完成之后,我们就可以进⾏验证了,编写测试类EnvNativeUtilsTest:
public class EnvNativeUtilsTest {
public static void main(String[] args){
EnvNativeUtils.setEnv("TEST_ENV_BY_JNI","Hello JNI");
System.out.v("TEST_ENV_BY_JNI"));
}
}
执⾏main⽅法,环境变量⽣效。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。