4

我有一个需要使用本机库的应用程序:libfoo.so

我的代码如下:

访问器.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 | <init>    in com.foo.bar.Accessor 

有趣的是,如果我将BuildConfig.groovy文件更改为 fork 模式,它也可以工作。

BuildConfig.groovy:

grails.project.fork = [
   run: [maxMemory:1024, minMemory:64, debug:false, maxPerm:256]
]

我不想在 fork 模式下运行它。

4

2 回答 2

3

我注意到正在使用两个不同的类加载器。

在非分叉模式下,使用了这个类加载器:java.net.URLClassLoader

在分叉模式下,使用了这个类加载器:groovy.lang.GroovyClassLoader

本机库在分叉模式下正常工作,所以我需要想出一个技巧来在非分叉模式下使用 GroovyClassLoader 加载库。

这是在 JDK 源代码中定义 System.load 的方式:

系统.java:

public final class System {
    ...
    public static void load(String filename) {
        Runtime.getRuntime().load0(getCallerClass(), filename);
    }
    ...
}

load0它使用类加载器和文件名调用。显而易见的解决方案是load0使用您自己的类加载器进行调用,但您不能调用它,因为它是受包保护的。

当您在 groovy 中编写代码时,您可以访问受包保护的私有方法/变量。

我可以指定自己的类加载器并加载库,如下所示:

class Accessor {        
    static {
        String path = "/usr/lib/libfoo.so"
        //System.load(path);
        Runtime.getRuntime().load0(groovy.lang.GroovyClassLoader.class, path)
    }
    ...
}

我刚试过,它在非分叉模式下工作。

于 2013-05-23T14:39:51.040 回答
0

我的猜测是,Accessor 类在同一个 JVM 内的不同类加载器中被多次加载(假设 grails 在与嵌入式 Tomcat 相同的 JVM 中运行)。通过将调试语句添加到静态块来测试这一点。

于 2013-05-22T18:59:36.510 回答