2016-08-01 34 views
1

Ich habe eine Java-Anwendung, die in Eclipse entwickelt wurde.ClassNotFoundException wenn außerhalb von IDE ausgeführt

Es gibt einen Ordner auf dem System, der viele ".java" -Dateien enthält. Diese ".java" -Dateien sind Klassen, die ein Benutzer geschrieben hat. Ich möchte alle diese Java-Klassen laden und sie in meiner Java-Anwendung kompilieren.

Eine weitere Eigenschaft aller ".java" -Dateien ist, dass alle darin enthaltenen Klassen eine Klasse erweitern, die sich in meiner ursprünglichen Anwendung befindet.

Ich habe Folgendes verwendet, um alle Klassen zu lesen und zu kompilieren.

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); 

Wenn ich den obigen Code in Eclipse ausführen, funktioniert es gut. Wenn ich die Anwendung als ein Gefäß aus anderen Ländern laufen, wirft es ein ClassNotFoundException für die Linie Class<? extends AbstractRule> cls = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);

Warum ist das passiert? Was ist anders, dass es in Eclipse ausgeführt wird und nicht über die Befehlszeile?

+0

Ist die IDE, die in Eclipse verwendet wird, dieselbe Version wie die, von der aus Sie JAR ausführen? Um Ihre Eclipse JRE zu überprüfen: Klicken Sie auf "Fenster" und dann auf "Einstellungen", wählen Sie unter Java im Menü Einstellungen die Option Installierte JREs. –

+0

Es gibt eine fehlende Klammer vor Class.forName. Tippfehler? – ManoDestra

+1

Bearbeitet. Das war ein Tippfehler.Keine Syntaxfehler in Eclipse – Kaushik

Antwort

1

Aus der Dokumentation für Class.forName

Namen - voll qualifizierte Namen der gewünschten Klasse

Also, um diese vollständig qualifizierten Klassennamen zu erhalten, müssen Sie Ihre rulesDir Variable manipulieren Ersetzen Sie die umgekehrten Schrägstriche durch Punkte, und fügen Sie diese der Variablen ruleName, kombiniert mit einem anderen Punkt, voran, um den vollständig qualifizierten Klassennamen zu erhalten. Dann können Sie den ClassLoader zum Laden der Klasse verwenden. Der vollständig qualifizierte Name ist erforderlich, damit der ClassLoader Ihre Ressource aus dem Stamm des Klassenpfads finden kann.

Hinweis: Ich nehme an, dass Ihr rulesDir ein relativer Pfad von der Basis Ihres Klassenpfads ist. Wenn dies nicht der Fall, dann werden Sie zusätzliche Manipulation zu tun haben hier

Siehe Code-Manipulation unter:

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); 
    } 
} 

Aus Gründen meines Tests wurde diese Klasse in dem Standard-Paket definiert und ich erstellt Ein Regelverzeichnis unterhalb dieser Ebene enthält meine Unterklassen von AbstractRule. Also, rules.TestRule war mein vollständig qualifizierter Pfad zu meinem Klassennamen. Aber, könnte Ihre sein ...

com.example.testapplication.rules.TestRule usw.

Und das ist, was in Class.forName erforderlich wäre. Es gibt einen Pfad zu Ihrem Klassenpfadstamm, dann den relativen Pfad von dort zu Ihren Java-Dateien (der dem Paket Ihrer Klassen entspricht), dann die tatsächlichen Klassennamen unter diesem Paketpfad.

+0

Entschuldigung. Die Annahme, die Sie haben, ist nicht korrekt. RulesDir ist ein absoluter Pfad. Kein relativer Pfad. Wie würden wir diese Situation lösen? @ManoDestra – Kaushik

+0

Ich habe Ihren Vorschlag versucht. Es funktioniert jetzt nicht auf Eclipse und der Befehlszeile. Aber ich denke, das liegt an der Annahme, dass rulesDir ein relativer Pfad ist. – Kaushik

+0

Ich habe in der Antwort beschrieben, wie diese Situation gelöst werden kann. Sie können auf Ihren absoluten Pfad für die Kompilierung zeigen, aber Sie müssen auf den vollständig qualifizierten Pfad für Class.forName zeigen, der eine periode-begrenzte Teilmenge des absoluten Pfads sein wird. – ManoDestra