2010-09-09 54 views
6

我有一个与一组第三方库进行交互的JNI库,并且系统上可能有多个版本的第三方库。对于第三方库的每个版本,为了可比性的原因,我必须重新编译JNI代码。现在我通过加载一个具有特定名称的DLL来处理这个问题,并且如果版本发生更改,我会更改JNI接口DLL的名称,以便版本的正确名称具有正确的名称以便加载。是否可以在Java中加载同一个DLL的不同版本?

我希望能够根据用户想要使用的版本动态加载dll。如果我在具有不同名称但方法签名相同的DLL上调用System.loadLibrary两次,会发生什么情况?

System.loadLibrary("JNIv1"); 
// Same code compiled against a different third party version 
System.loadLibrary("JNIv2"); 

我只需要一次使用其中一个版本,所以如果旧版本不再可访问,那很好。

是否有可能使用相同的方法签名加载两个不同版本的DLL,而无需重新启动程序?

回答

3

这是可能的,事实上它是完全支持和出色地工作。

我不得不在生产环境和太阳JVM上做到这一点,它坚如磐石。

基本上,如果从不同的类加载器加载库,那么它将加载库的不同副本。就这么简单。

我不会推荐这样做,除非你真的必须......但它确实有效。

作为一种替代方案,根据您的具体要求,您可以使其脱离进程,并且在客户端和不同服务器之间有一个简单的协议(使用jetty/xstream/httpclient或netty),每个它有一个不同的DLL版本加载。

实质上,这涉及你写一个类加载器

public class MyClassLoader extends URLClassLoader { 

    protected String findLibrary(String libName) { 
     if (libName.equals("mylib.dll")) { 
       return "full/path/to/library"; 
     } 
     else { 
       super.findLibrary(libName); 
     } 
    } 
} 

然后你安排加载使用相关的类加载器的类的实现......

public interface Implementation { 

} 

public class ImplementationLookerUpper { 

    Classloader v1 = new MyClassloader(version1); 
    Classloader v2 = new MyClassloader(version2); 


    public Implementation implementationUsingVersion(Version someversion) { 

      Classloader classloader = pickCorrectClassLoaderForVersion(someVersion); 

      return (Implementation) classloader.loadClass(RealImplementation.class.getName()).newInstance(); 

    } 
} 

这种事情.... 。

0

我不知道如何在Windows窗口中加载DLL的细节,但我的直觉告诉我,在同一时间加载重叠库肯定不会安全。但是,也许如果你第一次卸载第一个,可能是。

据我所知,没有办法明确卸载一个库。如果你看看java.lang.Classloader.NativeLibrary,你会发现当导致它被加载的类加载器被垃圾收集时(在它的finalize()方法中),库被卸载。因此,如果你将它加载到一个单独的类加载器中,然后等待它被垃圾收集(对System.gc()进行一些恶意调用以使其更快地发生),然后加载新的可能会有所帮助。

1

这不安全,实施起来相当麻烦。对于不同版本的dll,可能有不同的类加载器。你将不得不确保类加载器被垃圾回收,以确保DLL已被卸载。 (请参阅Java JNI - DLL Unloading

过去,我们通过编写处理用户请求的轻量级Java中介程序来解决此问题,并使用请求的dll版本创建新的子java程序。当用户请求不同的版本时,介体会关闭现有的孩子,并产生一个具有不同库路径的新孩子。这种方法运作良好。唯一的缺点是启动新的JVM需要花费时间,但是如果你有很多频繁出现的版本交换请求,这个问题才会显而易见。

相关问题