NoClassDefFoundError - 动态加载 Classpath Class 依赖加载到 URLClassLoader

NoClassDefFoundError - Dynamic loading Classpath Class dependency loaded in URLClassLoader

免责声明: 我知道有很多关于 class 加载的问题,但我还没有找到解决方案......

我的 class 路径上有一个 class,它依赖于动态加载的 class。现在我可以在 java 8 上加载 class,但在更高版本中它是一个问题。

在我的 class 路径上,我有包含 com.usoft.birt.ReportEngine class 的 c:/git/udevelop91/JAVA/usoft.jar。为了使 class 正常工作,我需要 c:/ReportEngine/lib 中的罐子。这些 jar 之一包含 PlatformConfig。当我单独加载 platformconfig 时,它工作正常并且 class 得到解决。当我尝试加载 ReportEngine class 时失败了。因为它依赖于LibraryClassLoader中的platformconfig。我也将 usoft.jar 添加到 LibraryClassLoader 以查看它是否有效,但它似乎一直在使用 AppClassLoader 而不是我的 LibraryClassLoader 导致 NoClassDefFoundError。如何在不从 class 路径中删除 usoft.jar 并且不将所有 birt jar 添加到 class 路径的情况下解决这个问题?所以带有 reportEngineInst 的行失败了

public void loadIt(){
    File f = new File("C:/ReportEngine/lib");
    File[] files = f.isDirectory() ? f.listFiles() : new File[] {f};
    URL[] urlss = new URL[files.length];
    int i = 0;
    for(File ff : files){
        urlss[i++] = ff.toURI().toURL();
    }

    LibraryClassLoader urlloader = new LibraryClassLoader(urlss, ClassLoader.getSystemClassLoader());   
    urlloader.addJar("c:/git/udev/JAVA/USoft.jar");

    Class<?> platformLoader = Class.forName("org.eclipse.birt.core.framework.PlatformConfig",true,urlloader);
    Class<?> report = urlloader.loadClass("com.usoft.birt.ReportEngine");
    // ---------- The line below fails with a NoClassDefFoundError ----------
    Object reportEngineInst = report.getDeclaredConstructor(String.class, String.class).newInstance("c:/ReportEngine", "c:/ReportEngine");
}

public class LibraryClassLoader extends URLClassLoader{
    public LibraryClassLoader( URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
    public void addJar(String path) throws MalformedURLException {
        super.addURL(Paths.get(path).toUri().toURL());
    }   
}

Simon 的评论帮助我意识到我无法使用 SystemClassLoader。要在类路径上继续使用具有相同 类 的当前解决方案并稍后添加特定的 类,我现在有:

public void loadIt() throws Exception{
    /*Instantiate list for own classloader*/
    ArrayList<URL> urlss = new ArrayList<>();

    /*First fix the default classpath dependencies. usoft.jar is normally on the classpath*/
    for(String pathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) {
        File fPathEntry = new File(pathEntry);
        File[] fPathEntries = fPathEntry.isDirectory() ? fPathEntry.listFiles() : new File[] {fPathEntry};
        for(File f : fPathEntries)
            urlss.add(f.toURI().toURL());
    }

    /*Now add the specific stuff*/
    File f = new File("C:/ReportEngine/lib");
    File[] files = f.isDirectory() ? f.listFiles() : new File[] {f};
    for(File ff : files)
        urlss.add(ff.toURI().toURL());

    /* 
       Make the classloader with the PlatformClassLoader as a parent 
       instead of the SystemClassLoader. I tried without a parent
       classloader, but then you cant find the platform classes 
       like java.sql.* 
    */
    URLClassLoader urlloader = new URLClassLoader(urlss.toArray(new URL[urlss.size()]), ClassLoader.getPlatformClassLoader());  

    /*And now it magically works*/
    Class<?> platformLoader = Class.forName("org.eclipse.birt.core.framework.PlatformConfig",true,urlloader);
    Class<?> report = Class.forName("com.usoft.birt.ReportEngine",true,urlloader);
    Object a = report.getDeclaredConstructor(String.class, String.class).newInstance("c:/reportEngine", "c:/reportEngine")  ;
    Method m = report.getMethod("generateDOC", String.class, String.class, Object[].class);
    m.invoke(a,"","",new Object[] {});
}