问题描述:
一个web应用中,需要用jni的方式调用DLL文件,用system.loadLibrary("kjdbc_jni")加载dll文件,项目在tomcat上运行。当我在开发过程中修改保存代码后,tomcat自动重启时报错,这个dll已经被其他加载器加载过
java.lang.UnsatisfiedLinkError: Native Library kjdbc_jni already loaded in another classloader
原因:
1.java虚拟机不允许一个JNI本地库同时被两个不同的classloader加载。
2.web服务器自动重启机制
当tomcat重启web应用时会自动加载dll, 但是重启web应用并不是重启整个tomcat,上一次启动的jvm仍然存在(jvm依然认为dll已经被之前的classloader加载过了),就不允许重启后web应用的classloader再去加载它。
但是手动重启tomcat,会将上一次启动的jvm关闭并重新启动,这样就可以正常加载。
解决方法:
虽然不同的web应用使用不同的classloader,但是所有web应用classloader的父classloader是同一个(BootstrapClassLoader)
启动类加载器BootstrapClassLoader:
是嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,启动类加载器无法被应用程序直接使用。
根据双亲委托模型,只要让父classloader加载jni本地库就可避免被多个classloader加载
具体做法:
将加载dll的代码抽出来,单独放到放到一个类里,写到一个static代码块,(加载这个类时就会先运行static代码块),然后将这个类变成一个jar文件,放到tomcat\lib文件夹下。再在web.xml中加一个监听,在项目启动的时候就加载这个类,加载dll.