使用Grails应用程序中的JNI本机库时出现UnsatisfiedLinkError
我有一个应用程序,我需要使用本机库: libfoo.so
我的代码如下:
Accessor.java:
public class Accessor { static { String path = "/usr/lib/libfoo.so"; System.load(path); } ... }
当我在独立的tomcat服务器中部署war文件时,这非常正常。
问题是当我运行时尝试运行嵌入式tomcat服务器时:
grails run-app
我得到一个UnsatisfiedLinkError:
Caused by UnsatisfiedLinkError: com.foo.bar.GFS_MALJNI.new_Accessor__SWIG_0(Ljava/lang/String;I)J ->> 39 | in com.foo.bar.Accessor
有趣的是,如果我将BuildConfig.groovy
文件更改为fork模式,它也可以工作。
BuildConfig.groovy:
grails.project.fork = [ run: [maxMemory:1024, minMemory:64, debug:false, maxPerm:256] ]
我不想在fork模式下运行它。
我注意到正在使用两个不同的类加载器。
在非分叉模式下,正在使用此类加载器: java.net.URLClassLoader
在分叉模式下,正在使用此类加载器: groovy.lang.GroovyClassLoader
本机库在分叉模式下正常工作,因此我需要在非分叉模式下使用GroovyClassLoader来加载库。
这是在JDK源中定义System.load的方式:
System.java:
public final class System { ... public static void load(String filename) { Runtime.getRuntime().load0(getCallerClass(), filename); } ... }
它使用类加载器和文件名调用load0
。 显而易见的解决方案是使用您自己的类加载器调用load0
,但由于它受包保护,因此您无法调用它。
在groovy中编写代码时,您可以访问packge保护的和私有的方法/变量。
我可以指定自己的类加载器并加载库,如下所示:
class Accessor { static { String path = "/usr/lib/libfoo.so" //System.load(path); Runtime.getRuntime().load0(groovy.lang.GroovyClassLoader.class, path) } ... }
我刚尝试过它,它在非分叉模式下工作。
我的猜测是,Accessor类在同一个JVM中的不同类加载器中被多次加载(假设grails在与嵌入式Tomcat相同的JVM中运行)。 通过将调试语句添加到静态块来测试它。