Classloader 卸载Java11中类加载器加载的dll

Classloader 卸载Java11中类加载器加载的dll,classloader,windows-authentication,java-11,finalize,sqljdbc,Classloader,Windows Authentication,Java 11,Finalize,Sqljdbc,我有一个场景,需要在SQL server上使用Windows身份验证两次使用Java11执行数据库连接。 最初,为第一次调用加载sqljdbc_auth.dll,连接成功。但是,为了第二次在不同的位置建立连接,它抛出了一个SQLException,表示“sqljdbc_auth.dll已由另一个类加载器加载”。 因此,我需要在两次调用之间卸载dll 在Java8版本之前,可以通过使用反射机制调用类加载器上的finalize()来执行相同的操作,但在Java11中找不到替代方法 示例代码: 在这里

我有一个场景,需要在SQL server上使用Windows身份验证两次使用Java11执行数据库连接。 最初,为第一次调用加载sqljdbc_auth.dll,连接成功。但是,为了第二次在不同的位置建立连接,它抛出了一个SQLException,表示“sqljdbc_auth.dll已由另一个类加载器加载”。 因此,我需要在两次调用之间卸载dll

在Java8版本之前,可以通过使用反射机制调用类加载器上的finalize()来执行相同的操作,但在Java11中找不到替代方法

示例代码:

在这里,我将sqljdbc_auth.dll放在路径中,并将名为sql_jdbc.jar的jar放在URL列表中,它们与Java11兼容

private static void loadFile(){
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();;
ClassLoader loader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); // here, urls is an array and contains only a single sql_jdbc.jar path in it
Thread.currentThread().setContextClassLoader(loader);
Driver driver = (Driver)loader.loadClass("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance();
Connection connection = driver.connect("jdbc:sqlserver://IP-addr:1433;DatabaseName=db_name;SelectMethod=cursor;integratedSecurity=true", props);
//perform db actions here
Thread.currentThread().setContextClassLoader(currentClassLoader);
unloadDLL("sqljdbc_auth.dll",loader);
}
private synchronized static void unloadDllFile(String dllName, ClassLoader classLoader) throws Throwable {
try {
    Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
    field.setAccessible(true);
    Map<Object, Object> lib = (ConcurrentHashMap<Object, Object>) field.get(classLoader);
    Set<Object> keyset = lib.keySet();
    for (Object dllpath : keyset) {
        if (dllpath.toString().contains(dllName)) {                    
            Object o = lib.get(dllpath);                    
            classLoader = null;
            field = null;
            lib.remove(dllpath);                     
            keyset.remove(dllpath);                   
            o = null;
            System.gc();                   
        }
     } 
  } catch (Exception e) {
        System.out.println("Exception in dll is "+e.getMessage());
         
  }
}
private static void loadFile(){
ClassLoader currentClassLoader=Thread.currentThread().getContextClassLoader();;
ClassLoader=new URLClassLoader(URL,ClassLoader.getSystemClassLoader());//这里,URL是一个数组,其中只包含一个sql_jdbc.jar路径
Thread.currentThread().setContextClassLoader(加载器);
Driver Driver=(Driver)loader.loadClass(“com.microsoft.sqlserver.jdbc.SQLServerDriver”).newInstance();
Connection=driver.connect(“jdbc:sqlserver://IP-addr:1433;DatabaseName=db_name;SelectMethod=cursor;integratedSecurity=true”,props);
//在此处执行数据库操作
Thread.currentThread().setContextClassLoader(currentClassLoader);
unloadDLL(“sqljdbc_auth.dll”,loader);
}
私有同步静态void unloadDllFile(字符串dllName,类加载器ClassLoader)抛出Throwable{
试一试{
Field=ClassLoader.class.getDeclaredField(“nativeLibraries”);
字段。setAccessible(true);
Map lib=(ConcurrentHashMap)field.get(classLoader);
Set keyset=lib.keyset();
for(对象dllpath:keyset){
如果(dllpath.toString().contains(dllName)){
对象o=lib.get(dllpath);
classLoader=null;
字段=空;
lib.remove(dllpath);
键集移除(dllpath);
o=零;
gc();
}
} 
}捕获(例外e){
System.out.println(“dll中的异常为”+e.getMessage());
}
}
第二个组件中有一个类似的代码,但它在那里抛出了一个异常。 在第二个组件中加载时的异常stacktrace为:

com.microsoft.sqlserver.jdbc.SQLServerException: This driver is not configured for integrated authentication. ClientConnectionId:d19de7a1-d099-477c-9c18-0c4cd5807f5e
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:2892)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<init>(AuthenticationJNI.java:72)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3636)
at com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:3627)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7194)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2935)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2456)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:2103)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1950)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1162)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:735)
at sample.java_samples.Sample2.loadFile(Sample2.java:66)
at sample.java_samples.Sample2.main(Sample2.java:23)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Windows\System32\sqljdbc_auth.dll already loaded in another classloader
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2456)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2649)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1867)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<clinit>(AuthenticationJNI.java:52)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3635)
... 10 more
com.microsoft.sqlserver.jdbc.SQLServerException:此驱动程序未配置为集成身份验证。客户连接ID:d19de7a1-d099-477c-9c18-0c4cd5807f5e
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:2892)
位于com.microsoft.sqlserver.jdbc.AuthenticationJNI。(AuthenticationJNI.java:72)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3636)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:3627)
位于com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7194)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2935)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2456)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:2103)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.connectioninternal(SQLServerConnection.java:1950)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1162)
位于com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:735)
位于sample.java_samples.Sample2.loadFile(Sample2.java:66)
位于sample.java_samples.Sample2.main(Sample2.java:23)
原因:java.lang.UnsatifiedLink错误:本机库C:\Windows\System32\sqljdbc\u auth.dll已加载到另一个类加载器中
位于java.base/java.lang.ClassLoader$NativelLibrary.loadLibrary(ClassLoader.java:2456)
位于java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684)
位于java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2649)
位于java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
位于java.base/java.lang.System.loadLibrary(System.java:1867)
位于com.microsoft.sqlserver.jdbc.AuthenticationJNI。(AuthenticationJNI.java:52)
位于com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3635)
... 10多

谢谢

为什么不重用
驱动程序
来创建新的集合?@Holger,我没有选择重用驱动程序,因为这是软件两个不同组件中的调用。您能建议在这种情况下是否有这样做的方法吗?因为您说过您使用hack在旧Java版本的类加载器上调用
finalize()
,所以您必须能够访问类加载器。然后,您还可以访问驱动程序。@Holger,是的,我过去常常在第一次连接完成后立即调用finalize(),目的在第一个组件中实现,因此在以后的组件中后续连接没有问题。通过修复应用程序设计,您可以节省大量时间,共享数据库驱动程序。