解析Java和Eclipse中加载本地库(.dll文件)的详细说明
程序员文章站
2023-12-13 11:41:46
最近在做的工作要用到本地方法,需要在java中加载不少动态链接库(以下为方便延用windows平台下的简写dll,但并不局限于windows)。刚刚把程序跑通,赶紧把一些心...
最近在做的工作要用到本地方法,需要在java中加载不少动态链接库(以下为方便延用windows平台下的简写dll,但并不局限于windows)。刚刚把程序跑通,赶紧把一些心得写出来,mark。也希望对大家的类似工作有所帮助
首先,应当明确,dll有两类:(1)java所依赖的dll和,(2)dll所依赖的dll。正是由于第(2)种dll的存在,才导致了java中加载dll的复杂性大大增加,许多说法都是这样的,但我实验的结果却表明似乎没有那么复杂,后面会予以详细阐述。
其次,java中加载dll的方式也有两种:(1)通过调用system.loadlibrary(string filename)和,(2)通过调用system.load(string filename)方法。其底层都是通过使用classloader中的loadlibrary(class fromclass, string name, boolean isabsolute)方法来实现的,区别仅在于(1)中的filename必须是绝对路径,(2)中的filename只能是dll名,不允许包含文件夹。
再者,eclipse是一个相当强大的平台,其提供的bundleclassloader的强大是一个很重要的原因,对于dll的加载也有自己一套很别致的做法,值得我们采纳。
根据上面的介绍,分两部分阐述java中加载dll面临的主要问题和解决途径。
1. 在一般java程序中加载dll
我所做的工作,需要加载的dll如下:
digitdll.dll
dsivsacct.dll
dsivscomm.dll
dsivstrans.dll
jbpack.dll
xcodedll.dll
imagedllctrl.dll
yhfiche.dll
yhocr.dll
yhbill.dll
tsealsvrdll.dll
timg.dll
timage.dll
直接调用的是timage中的若干方法,列表中timage之前的所有其直接或间接依赖的,不仅要把所有的dll load全,更要注意他们之间的依赖关系,被依赖dll一定要先加载,否则就会报错:unsatisfiedlinkerror。故而,首先应理清dll 之间的依赖关系,上面的列表已经是处理过的了。
接下来是设置jvm的搜索路径,使其能够找到你的dll。jvm的搜索路径由java.library.path系统属性决定,其默认值为系统环境变量中的path 内容。因此,可以通过修改path变量来达到设置java.library.path属性的目的(改变之后eclipse需要重新启动),一般的方法是在 path中加入dll所在文件夹的绝对路径。另一种方法是在java命令的参数中加入“-djava.library.path=dll所在文件夹的绝对路径”来设置(可以用;分开多个路径)。对于eclipse开发环境上的应用程序,可以通过修改其启动参数,在vm arguments编辑框中加入前述参数。对于打包出来的eclipse安装包,可编辑其启动目录下的application.ini(假设其启动文件为 application.exe),在-vmargs后加入前述参数来设置java.library.path的值。需要注意的是,一旦jvm已经启动,则无法再修改java.library.path的内容了,也就是说,通过:
system.setproperty("java.library.path", "c:/mylib");
这样的方式是无法达到目的的,因为该属性是只读的。sun公司的论坛上曾经讨论过如何在代码中修改java.library.path的问题,结论是:不能通过代码修改!如果嫌"java -djava.library.path=c:/mylib"这样的方式写得太死,也只能是通过shell编程之类的方法对路径进行预处理,以改善其灵活性了。
如果你的dll是封装在jar包中的,则需要首先将之解压缩到一个临时路径上,然后再将该路径加入到djava.library.path中,或者干脆将其解压缩到系统路径上。
2. 在eclipse平台上加载dll
上面提到,java中对本地库路径的设置方式做得太死,这也是我自己的切身体会,但令人感到欣慰的是我们的eclipse平台的提供了一套比较灵活的做法,通过eclipse提供的bundleclassloader,你可以将dll封在plugin中,既不需要在使用时解压缩,也不需要额外设置 java.library.path属性,bundleclassloader会自行到以相对plugin根目录的指定目录下去查找你的dll,这些目录是:ws/win32/, os/win32/x86/, os/win32/, nl/zh/cn/, nl/zh/,见org.eclipse.osgi.internal.baseadaptor.defaultclassloader和 org.eclipse.core.runtime.internal.adaptor.eclipseclassloadinghook。
我的目录设置是:
.classpath
.cvsignore
.project
build.properties
classes
cvs
lib
meta-inf
os
plugin.xml
src
我把所有的dll都放到了os下面的win32目录内,同样可以建立ws/win32等目录用于放置本地库。如此处理之后,不用再修改任何系统变量就可以顺利加载本地库了。
另外,eclipse还在manifest文件中提供了bundle-nativecode的设置项,也是用于加载本地库的,有待进一步研究
本文匆匆而就,希望对自己对大家都能有所帮助
首先,应当明确,dll有两类:(1)java所依赖的dll和,(2)dll所依赖的dll。正是由于第(2)种dll的存在,才导致了java中加载dll的复杂性大大增加,许多说法都是这样的,但我实验的结果却表明似乎没有那么复杂,后面会予以详细阐述。
其次,java中加载dll的方式也有两种:(1)通过调用system.loadlibrary(string filename)和,(2)通过调用system.load(string filename)方法。其底层都是通过使用classloader中的loadlibrary(class fromclass, string name, boolean isabsolute)方法来实现的,区别仅在于(1)中的filename必须是绝对路径,(2)中的filename只能是dll名,不允许包含文件夹。
再者,eclipse是一个相当强大的平台,其提供的bundleclassloader的强大是一个很重要的原因,对于dll的加载也有自己一套很别致的做法,值得我们采纳。
根据上面的介绍,分两部分阐述java中加载dll面临的主要问题和解决途径。
1. 在一般java程序中加载dll
我所做的工作,需要加载的dll如下:
digitdll.dll
dsivsacct.dll
dsivscomm.dll
dsivstrans.dll
jbpack.dll
xcodedll.dll
imagedllctrl.dll
yhfiche.dll
yhocr.dll
yhbill.dll
tsealsvrdll.dll
timg.dll
timage.dll
直接调用的是timage中的若干方法,列表中timage之前的所有其直接或间接依赖的,不仅要把所有的dll load全,更要注意他们之间的依赖关系,被依赖dll一定要先加载,否则就会报错:unsatisfiedlinkerror。故而,首先应理清dll 之间的依赖关系,上面的列表已经是处理过的了。
接下来是设置jvm的搜索路径,使其能够找到你的dll。jvm的搜索路径由java.library.path系统属性决定,其默认值为系统环境变量中的path 内容。因此,可以通过修改path变量来达到设置java.library.path属性的目的(改变之后eclipse需要重新启动),一般的方法是在 path中加入dll所在文件夹的绝对路径。另一种方法是在java命令的参数中加入“-djava.library.path=dll所在文件夹的绝对路径”来设置(可以用;分开多个路径)。对于eclipse开发环境上的应用程序,可以通过修改其启动参数,在vm arguments编辑框中加入前述参数。对于打包出来的eclipse安装包,可编辑其启动目录下的application.ini(假设其启动文件为 application.exe),在-vmargs后加入前述参数来设置java.library.path的值。需要注意的是,一旦jvm已经启动,则无法再修改java.library.path的内容了,也就是说,通过:
system.setproperty("java.library.path", "c:/mylib");
这样的方式是无法达到目的的,因为该属性是只读的。sun公司的论坛上曾经讨论过如何在代码中修改java.library.path的问题,结论是:不能通过代码修改!如果嫌"java -djava.library.path=c:/mylib"这样的方式写得太死,也只能是通过shell编程之类的方法对路径进行预处理,以改善其灵活性了。
如果你的dll是封装在jar包中的,则需要首先将之解压缩到一个临时路径上,然后再将该路径加入到djava.library.path中,或者干脆将其解压缩到系统路径上。
2. 在eclipse平台上加载dll
上面提到,java中对本地库路径的设置方式做得太死,这也是我自己的切身体会,但令人感到欣慰的是我们的eclipse平台的提供了一套比较灵活的做法,通过eclipse提供的bundleclassloader,你可以将dll封在plugin中,既不需要在使用时解压缩,也不需要额外设置 java.library.path属性,bundleclassloader会自行到以相对plugin根目录的指定目录下去查找你的dll,这些目录是:ws/win32/, os/win32/x86/, os/win32/, nl/zh/cn/, nl/zh/,见org.eclipse.osgi.internal.baseadaptor.defaultclassloader和 org.eclipse.core.runtime.internal.adaptor.eclipseclassloadinghook。
我的目录设置是:
.classpath
.cvsignore
.project
build.properties
classes
cvs
lib
meta-inf
os
plugin.xml
src
我把所有的dll都放到了os下面的win32目录内,同样可以建立ws/win32等目录用于放置本地库。如此处理之后,不用再修改任何系统变量就可以顺利加载本地库了。
另外,eclipse还在manifest文件中提供了bundle-nativecode的设置项,也是用于加载本地库的,有待进一步研究
本文匆匆而就,希望对自己对大家都能有所帮助