资讯详情

Springboot+JNI调用C编写的动态库

简介

JNI是Java本机接口(Java Native Interface),是本机编程接口,是本机编程接口Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。JNI允许Java用其他语言编写代码和代码库。Invocation API(JNI可用于未来Java虚拟机(JVM)嵌入本机应用程序,允许程序员从本机代码中调用Java代码。

Java调用动态库的步骤一般如下:

以下是逐步实现的:

一、编写Java类

public class JniSignLib {     static {         System.loadLibrary("certlib");     }      public static native byte[] sign(String profile, byte[] data, int datalen);     public static native int check(String certpath, byte[] data, int datalen, byte[] sigdata, int siglen);     public static native int certFileCheck(String certfile);     public static native int certVerify(String certPath, String caPath);     public static native CertInfo getcertinfo(String certPath); }

对应的一个javabean

public class CertInfo {     String c_begindate;     String c_enddate;     String c_subject;     String c_issuer;     String c_sn;     String c_ver; }

二、生成class文件

进入工程的src\main\java\ 执行命令的目录

javah -classpath . -jni com.test.jni.JniSignLib

在java生成了类目录class文件。

三、生成.h文件

还是在java在目录下执行命令

javah -classpath . -jni -encoding UTF-8 com.test.jni.JniSignLib

在工程的java响应将在目录下生成com_test_jni_JniSignLib.h文件

四、实现c 方法

这里着重讲一下函数的返回值,

第一个函数返回java的byte[],从C 的char转换过来

JNIEXPORT jbyteArray  Java_com_test_jni_JniSignLib_sign(JNIEnv *env, jclass obj, jstring prifile, jbyteArray data, jint datalen) {     int rv=0;     jboolean b = true;     unsigned char * pindata=NULL;     unsigned char   sigdata[64]={0};     unsigned int    siglen=0;      const char *pk = env->GetStringUTFChars(prifile, &b);     jbyte *pdata = env->GetByteArrayElements(data,0);     pindata= (unsigned char *)malloc(datalen);     memcpy(pindata,pdata,datalen);     rv=edge_sign((char*)pk,pindata,datalen,sigdata,&siglen);//     if (rv!=0)     {         free(pindata);         return NULL;     }     free(pindata);      // 返回给java的byte[]     jbyteArray array = env->NewByteArray(64);     env->SetByteArrayRegion(array, 0, 64, reinterpret_cast<jbyte *>(sigdata));     return array; }

第二个是Java的CertInfo,通过,env构建一个对象,然后通过C 中的结果给bean每个字段赋值

JNIEXPORT jobject  Java_com_test_jni_JniSignLib_getcertinfo(JNIEnv *env, jclass obj, jstring certpath)  {   int rv=0;   C_INFO certinfo;   jboolean b = true;   const char *certPtr = env->GetStringUTFChars(certpath, &b);   rv=getcertinfo(certPtr,certinfo);   if (rv!=0)   {    return NULL;   }      jobject objValue = NULL;      //获取Java中的实例类    jclass objectClass = (env)->FindClass("com/test/jni/CertInfo");         // 获取构建函数的新对象    jmethodID construct_Result = env->GetMethodID(objectClass, "<init>", "()V");    objValue = env->NewObject(objectClass, construct_Result, "");         ////每个变量在获取类中的定义    jfieldID bdt = (env)->GetFieldID(objectClass,"c_begindate","Ljava/lang/String;");    jfieldID edt = (env)->GetFieldID(objectClass,"c_enddate","Ljava/lang/String;");    jfieldID sub = (env)->GetFieldID(objectClass,"c_subject","Ljava/lang/String;");    jfieldID iss = (env)->GetFieldID(objectClass,"c_issuer","Ljava/lang/String;");    jfieldID csn = (env)->GetFieldID(objectClass,"c_sn","Ljava/lang/String;");    jfieldID ver = (env)->GetFieldID(objectClass,"c_ver","Ljava/lang/String;");     ///给每个例子的变量付值    (env)->SetObjectField(objValue,bdt,(env)->NewStringUTF(certinfo.c_begindate));    (env)->SetObjectField(objValue,edt,(env)->NewStringUTF(certinfo.c_enddate));    (env)->SetObjectField(objValue,sub,(env)->NewStringUTF(certinfo.c_subject));    (env)->SetObjectField(objValue,iss,(env)->NewStringUTF(certinfo.c_issuer));    (env)->SetObjectField(objValue,csn,(env)->NewStringUTF(certinfo.c_sn));    (env)->SetObjectField(objValue,ver,(env)->NewStringUTF(certinfo.c_ver));        return objValue;  } 

五、打包成动态库

这里要注意的是32还是64,和Java环境一致。

六、打包Java工程

这里坑多。

将打包好的dll或so项目中的文件resources目录下,

在最开始的jni写在定义中System.loadLibrary("certlib");

在实际打成jar这个文件在包运行时找不到。jar包运行时resources文件不会存储在本地路径中,因此需要以不同的方式读取动态库,并建立新的工具类:

public class LibLoader {     public staticvoid loadLib(String libName) {
        String resourcePath = "/" + libName;
        String folderName = System.getProperty("java.io.tmpdir") + "/lib/";
        File folder = new File(folderName);
        folder.mkdirs();
        File libFile = new File(folder, libName);
        if (libFile.exists()) {
            System.load(libFile.getAbsolutePath());
        } else {
            try {
                InputStream in = LibLoader.class.getResourceAsStream(resourcePath);
                FileUtils.copyInputStreamToFile(in, libFile);
                in.close();
                System.load(libFile.getAbsolutePath());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Failed to load required lib", e);
            }
        }
    }
}

大致原理就是使用JavaClass的getResourceAsStream函数获取动态库文件的流,然后将这个流保存在本地路径中,最后使用System.load从本地路径读取动态库。

修改JniSignLib

public class JniSignLib {
    static {
        LibLoader.loadLib("libsecurejni.so");
    }

    public static native byte[] sign(String profile, byte[] data, int datalen);
    public static native int check(String certpath, byte[] data, int datalen, byte[] sigdata, int siglen);
    public static native int certFileCheck(String certfile);
    public static native int certVerify(String certPath, String caPath);
    public static native CertInfo getcertinfo(String certPath);
}

另外还有一点,如果在windows环境下,dll引用的windows的动态库,如msvcr100d.dll,这个库放到resources目录下也找不到,得放到window的system32下面去。同样如果在linux环境下的系统库,也得放到/lib64或/usr/lib64这些目录下。

最后的工程目录如下

 

标签: bdt64c晶体管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台