java调用JNI笔记

一、环境

CentOS7+jdk1.8
开发:idea2021

二、java调用指令

pom.xml
java调用JNI需要依赖外部的jar包,pom.xml引用如下,我使用的4.3.0版本。

<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.3.0</version>
</dependency>

JNI定义

package com.fly.api;
import com.sun.jna.Library;
import com.sun.jna.Native;
/**
 * 其他的业务接口就不再此展示,仅保留两个示例接口
 * @author fly
 */
public interface TLibrary extends Library {
    public TLibrary instance = (TLibrary) Native.loadLibrary("SID",TLibrary.class);
    /**
     * 开启线程
     * @return
     */
    int TStart(int threadNum);
}

业务调用

private void action(){
    //接口调用
    Integer val= com.fly.api.TLibrary.instance.TStart(4);
    log.info("TStart.returnVal="+val);
}

三、Native.loadLibrary部分源码

入口库就是我们java直接调用的c++库。经常会遇到在在各种目录下的各种问题。我们先看下Native.loadLibrary的核心实现逻辑。
Native.loadLibrary入口类源码:

public static <T> T loadLibrary(String name, Class<T> interfaceClass) {
    return loadLibrary(name, interfaceClass, Collections.<String, Object>emptyMap());
}

name说白了就是类库文件名(win系统.dll文件,linux系统为.so文件)
注意:name需要去前缀lib。不论是win系统还是linux都一样的。如文件全名为:libTlibrary.dll(libTlibrary.so)则name="Tlibrary",lib前缀为底层约定。win下可以不带lib,源码就不在此展开了分析了。
Native.loadLibrary核心逻辑说白了就是通过name找类库文件,找到文件后,通过解析与java接口中的方法一一对应。
注:关于入口库文件的位置可以通过配置jna.library.path来修改所在目录。jna.library.path是通过System.getProperty("jna.library.path", "")。结合应用及系统自行配置就可以。我在应用中并没有配置此属性,不配置相关属性会通过类加载器去尝试加载类库。所以我将类库放在了应用的资源文件中。
在loadLibrary中关键代码:

File embedded = Native.extractFromResourcePath(libraryName, (ClassLoader)options.get(Library.OPTION_CLASSLOADER));

对应extractFromResourcePath方法如下:

public static File extractFromResourcePath(String name, ClassLoader loader) throws IOException {
    final boolean DEBUG = DEBUG_LOAD
        || (DEBUG_JNA_LOAD && name.indexOf("jnidispatch") != -1);
    if (loader == null) {
        loader = Thread.currentThread().getContextClassLoader();
        // Context class loader is not guaranteed to be set
        if (loader == null) {
            loader = Native.class.getClassLoader();
        }
    }
    if (DEBUG) {
        System.out.println("Looking in classpath from " + loader + " for " + name);
    }
    String libname = name.startsWith("/") ? name : NativeLibrary.mapSharedLibraryName(name);
    String resourcePath = name.startsWith("/") ? name : Platform.RESOURCE_PREFIX + "/" + libname;
    if (resourcePath.startsWith("/")) {
        resourcePath = resourcePath.substring(1);
    }
    URL url = loader.getResource(resourcePath);
    if (url == null && resourcePath.startsWith(Platform.RESOURCE_PREFIX)) {
        // If not found with the standard resource prefix, try without it
        url = loader.getResource(libname);
    }
    if (url == null) {
        String path = System.getProperty("java.class.path");
        if (loader instanceof URLClassLoader) {
            path = Arrays.asList(((URLClassLoader)loader).getURLs()).toString();
        }
        throw new IOException("Native library (" + resourcePath + ") not found in resource path (" + path + ")");
    }
    if (DEBUG) {
        System.out.println("Found library resource at " + url);
    }
        File lib = null;
        if (url.getProtocol().toLowerCase().equals("file")) {
            try {
                lib = new File(new URI(url.toString()));
            }
            catch(URISyntaxException e) {
                lib = new File(url.getPath());
            }
            if (DEBUG) {
                System.out.println("Looking in " + lib.getAbsolutePath());
            }
            if (!lib.exists()) {
                throw new IOException("File URL " + url + " could not be properly decoded");
            }
        }
        else if (!Boolean.getBoolean("jna.nounpack")) {
            InputStream is = loader.getResourceAsStream(resourcePath);
            if (is == null) {
                throw new IOException("Can't obtain InputStream for " + resourcePath);
            }
            FileOutputStream fos = null;
            try {
                // Suffix is required on windows, or library fails to load
                // Let Java pick the suffix, except on windows, to avoid
                // problems with Web Start.
                File dir = getTempDir();
                lib = File.createTempFile(JNA_TMPLIB_PREFIX, Platform.isWindows()?".dll":null, dir);
                if (!Boolean.getBoolean("jnidispatch.preserve")) {
                    lib.deleteOnExit();
                }
                fos = new FileOutputStream(lib);
                int count;
                byte[] buf = new byte[1024];
                while ((count = is.read(buf, 0, buf.length)) > 0) {
                    fos.write(buf, 0, count);
                }
            }
            catch(IOException e) {
                throw new IOException("Failed to create temporary file for " + name + " library: " + e.getMessage());
            }
            finally {
                try { is.close(); } catch(IOException e) { }
                if (fos != null) {
                    try { fos.close(); } catch(IOException e) { }
                }
            }
        }
        return lib;
    }

四、资源文件

入口类库

jni入口类库放在应用程序的资源目录下,具体的还要和开发、部署的系统有关系。
如我部署的centos 64位,就需要在根资源目录上创建一个linux-x86-64的文件夹,将.so文件放在linux-x86-64文件夹中。

依赖类库

如果入口类库有依赖的类库,则需要将依赖的类库放在jre的相关目录中,并赋予可执行权限。
jre安装目录:/usr/local/java/jdk1.8.0_121/jre
则需要将依赖的类库放在目录:/usr/local/java/jdk1.8.0_121/jre/lib/amd64

posted @ 2021-04-20 14:02  班然  阅读(545)  评论(0编辑  收藏  举报