如何通过Java代码影响System.loadLibrary()的搜索路径?

2022-09-01 21:23:49

在Java项目中,我正在使用第三方库,该库通过以下方式加载一些本机库

System.loadLibrary("libName");

我希望能够从我的应用程序中影响此方法的搜索路径,以便用户不需要在命令行上指定正确的java.library.path值(此值取决于当前的操作系统和体系结构)。例如,在Windows上,我想将其设置为“lib/native/windows”,在Linux 32bit上设置为“lib/native/linux32”等。

我试过了

System.setProperty("java.library.path", ...)

但这被忽略了,显然是因为JVM在我的代码运行之前只读取此属性一次。

我还尝试在使用依赖于它的Java库之前加载本机libray

System.load("fullPath/lib")

此调用成功,但当使用 System.loadLibrary() 再次加载本机库时,仍会出现一个不满意的 LinkError。

我发现的唯一方法是:

  • 添加抽象化外部库的整个 API 的接口。
  • 在代码的其余部分中仅使用这些接口。
  • 添加实现接口并委托到库的类。
  • 编写自己的类加载器,即
    • 覆盖 findLibary(), 以便在正确的路径中找到本机库
    • 覆盖 loadClass() 并加载外部库和包装层的所有类,而不是像默认的 ClassLoader 那样尝试委托给其父级
  • 确保使用正常的 ClassLoader 加载接口,并使用我自己的 ClassLoader 加载包装类和外部库。

这有效,但我发现它非常复杂,而且需要付出很多努力,因为我需要添加所有这些接口。有没有更简单的方法?


答案 1

我需要更改单元测试的 dll 路径。我尝试了以下技巧,它的工作原理:

System.setProperty( "java.library.path", "/path/to/libs" ); 
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );

有关说明,请参阅原始链接


答案 2
  1. 没有经过批准的方法可以更改正在运行的 JVM 的库路径。
  2. 您不能多次加载本机库...并且您无法卸载本机库,以便可以再次加载它:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986

根据您上面的评论(特别是第三方库的行为),我想说您最好的选择是在启动JVM时获得正确的库路径。

请注意,有一种黑客方法可以更改库路径(请参阅 https://stackoverflow.com/a/24258955/139985),但它涉及令人讨厌的反射,据报道它不适用于所有Java版本。当然,它依赖于 ClassLoader 的未记录的私有实现细节,这些细节可能会从一个版本更改为下一个版本。


推荐