代码之家  ›  专栏  ›  技术社区  ›  C. Ross trotttrotttrott

使用Java反射与LBPATH协同工作

  •  -1
  • C. Ross trotttrotttrott  · 技术社区  · 14 年前

    我正在动态加载一个类并对其调用一个方法。这个班做JNI。当我调用类时,Java尝试加载库。这会导致错误,因为库不在libpath上。我从一个jar调用,所以我不能轻易地更改libpath(特别是因为库不在jar的同一目录或子目录中)。我确实知道库的路径,但是我如何在加载类之前加载它。

    当前代码:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if(!CLASS_NAME.equals(name))
                return super.loadClass(name);
    
        try {
            URL myUrl = new URL(classFileUrl);
            URLConnection connection = myUrl.openConnection();
            InputStream input = connection.getInputStream();
            byte[] classData = readConnectionToArray(input);
    
            return defineClass(CLASS_NAME,
                    classData, 0, classData.length);
    
        } catch (MalformedURLException e) {
            throw new UndeclaredThrowableException(e);
        } catch (IOException e) {
            throw new UndeclaredThrowableException(e); 
        }
    }
    

    例外:

    Can't find library libvcommon.so
    java.lang.UnsatisfiedLinkError: vcommon (A file or directory in the path name does not exist.)
            at java.lang.ClassLoader.loadLibraryWithPath(ClassLoader.java:998)
            at java.lang.ClassLoader.loadLibraryWithClassLoader(ClassLoader.java:962)
            at java.lang.System.loadLibrary(System.java:465)
            at vcommon.(vcommon.java:103)
            at java.lang.J9VMInternals.initializeImpl(Native Method)
            at java.lang.J9VMInternals.initialize(J9VMInternals.java:200)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
            at java.lang.reflect.Method.invoke(Method.java:599)
            at com.fortune500.fin.v.vunit.reflection.ReflectionvProcessor.calculateV(ReflectionvProcessor.java:36)
            at com.fortune500.fin.v.vunit.UTLTestCase.execute(UTLTestCase.java:42)
            at com.fortune500.fin.v.vunit.TestSuite.execute(TestSuite.java:15)
            at com.fortune500.fin.v.vunit.batch.Testvendor.execute(Testvendor.java:101)
            at com.fortune500.fin.v.vunit.batch.Testvendor.main(Testvendor.java:58)
    

    编辑: 我现在有一个64位对32位的问题。等我把事情解决了,我再谈这个。

    相关: Dynamic loading a class in java with a different package name

    1 回复  |  直到 14 年前
        1
  •  1
  •   mdma    14 年前

    如果知道库的路径,可以将该路径添加到 java.library.path 自定义类加载器中的环境变量。一种更简单的方法是计算路径并在调用 Runtime.loadLibrary .

    下面的代码概述了两种方法,使用LoadLibrary和设置java.library.path系统属性。

     if(CLASS_NAME.equals(name)) {
    
        // two ways of doing this - either load the library explicitly from the full path  
        if (useFullPath) {
          Runtime.getRuntime().loadLibrary("/full/path/to/mylibrary");
        }
        else { // or tweaking the library path
          System.setProperty("java.library.path", 
             System.getProperty("java.library.path")
             + System.getProperty("file.separator")
             + "/path/to/lib");
        }
     }
     return super.loadClass(name);
    

    您提到您的代码是从JAR调用的——如果类加载器也是JAR的一部分,那么使用自定义类加载器将会很困难。您是否确认类加载器确实在使用中?

    一种更简单的方法是将对本机类中LoadLibrary的当前调用更改为使用完整路径。例如,从系统属性中获取,或者计算它,如果您事先知道在哪里可以找到它。当然,如果您有本机类的源代码,那么这只是一个选项。如果您不能修改本机类,那么在类加载器中使用loadLibrary调用。

    我的理解是,调用加载具有相同库名(不管路径如何)的库将加载相同的库。(至少,这是Windows上的行为-我还没有在Linux上验证。)因此,即使类加载器使用完整路径加载库,而本机类使用简单名称加载库,两者都应该解析为同一个库。

    (为了完整性起见,解决等效库同样发生在内核中,这是从win32 experience说起的。每个库在内部都有一个名称,而Windows只为每个进程加载一个具有相同内部名称的库实例。)