运行 在 IDE 之外时发生 ClassNotFoundException

ClassNotFoundException when running outside IDE

我有一个在 Eclipse 中开发的 java 应用程序。

系统上有一个文件夹,里面有很多“.java”文件。这些“.java”文件是某些用户编写的 classes。我希望加载所有这些 java classes 并在我的 java 应用程序中编译它们。

所有“.java”文件中的另一个 属性 是所有写在里面的 class 扩展了我原来的应用程序中的 class。

我使用以下内容阅读和编译所有 classes.

</p> <pre><code>File parentFile = new File(rulesDir + "\"); String fileName = rulesDir + "\" + ruleName + ".java"; File ruleFile = new File(fileName); // Compile source file. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, null, null, ruleFile.getPath()); // Load and instantiate compiled class. URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentFile.toURI().toURL() }); Class<? extends AbstractRule> cls = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);

如果我在 Eclipse 中 运行 上面的代码,它工作正常。当我 运行 应用程序作为来自其他地方的 jar 时,它会为该行抛出一个 <code>ClassNotFoundException <code>Class<? extends AbstractRule> cls = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);

为什么会这样?它在 Eclipse 中执行与不通过命令行执行有何不同?

来自 Class.forName

的文档

name - fully qualified name of the desired class

因此,为了获得完全限定的 class 名称,您需要操作 rulesDir 变量以用句点替换反斜杠,然后将其添加到您的 ruleName 变量中,再与另一个句点结合使用,以获得完全限定的 class 名称。然后您就可以使用 ClassLoader 加载 class。需要完全限定名称,以便 ClassLoader 可以从您的 class 路径的根目录中找到您的资源。

注意我假设您的 rulesDir 是从您的 class 路径开始的相对路径。如果不是,那么您将需要在此处进行额外的操作

查看下面的代码操作:

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import rules.AbstractRule;

public class MainApp {
    public static void main(String[] args) {
        try {
            System.out.println("Test");
            // NB Amended here to now take project root, relative path to rules directory and class name. So that compilation can take place based on the absolute path and class loading from the relative one.
            compile("C:\Media\Code\manodestra_java\src\tmp", "rules", "TestRule");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void compile(String projectRoot, String rulesDir, String ruleName) throws Exception {
        File parentFile = new File(projectRoot + "\" + rulesDir + "\");
        System.out.println(parentFile);
        String fileName = parentFile.getCanonicalPath() + "\" + ruleName + ".java";
        File ruleFile = new File(fileName);
        System.out.println(ruleFile);

        // Compile source file.
        System.out.println("Compiling...");
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        compiler.run(null, null, null, ruleFile.getPath());

        // Load and instantiate compiled class.
        System.out.println("Loading class...");
        URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentFile.toURI().toURL() });
        System.out.println("Class Loader: " + classLoader);
        ruleName = rulesDir.replace("\", ".") + "." + ruleName;
        Class<? extends AbstractRule> clazz = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);
        System.out.println(clazz);
    }
}

为了我的测试,这个 class 是在默认包中定义的,我在该级别下创建了一个规则目录以包含我的 AbstractRule 的子classes。所以,rules.TestRule 是我 class 名字的完全限定路径。但是,你的可能是...

com.example.testapplication.rules.TestRule, etc.

这就是 Class.forName 中的要求。有一个到你的 classpath root 的路径,然后是从那里到你的 java 文件的相对路径(相当于你的 classes 的包),然后是实际的 class 该包路径下的名称。