一、环境
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