2010-05-21 3 views
3

Je l'extrait suivant du code java:classloading dynamique échoue sur l'exécution

final Class<?> junitCoreClass = AccessController.doPrivileged(
    new PrivilegedAction<URLClassLoader>() { 
     @Override 
     public URLClassLoader run() { 
     return new URLClassLoader(new URL[] { junitJarUrl }); 
     } 
    }).loadClass("org.junit.runner.JUnitCore"); 

System.out.println(junitCoreClass.getName()); 
final JUnitCore junitCore = (JUnitCore) junitCoreClass.newInstance(); 

Cette compile très bien. Mais quand j'essaie de l'exécuter, quelque chose d'étrange arrive; un java.lang.NoClassDefFoundError est lancé sur cette dernière ligne, en référence à la classe qui vient d'être chargée. La partie étrange est, le println imprime le nom de classe exact.

J'ai vérifié que si je garde la nouvelle référence d'instance Object et la manipule uniquement par réflexion, tout va bien, donc le morceau de code incriminé doit être la distribution explicite. Est-ce que quelqu'un peut m'expliquer pourquoi cela arrive, et aussi me dire comment je peux réaliser ce que j'essaie de faire?

PS: Pour ceux qui veulent voir une trace de la pile de plus près, il n'y a pas grand chose à montrer:

 
java.lang.NoClassDefFoundError: org/junit/runner/JUnitCore 
    at [last line of example) 
    [lines from my app] 
Caused by: java.lang.ClassNotFoundException: org.junit.runner.JUnitCore 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:200) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:315) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:330) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:250) 
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:398) 
    at [last line of example] 
    [lines from my app] 
+0

Veuillez nous montrer la pile complète pour l'exception. –

Répondre

4

Le problème est que votre classe principale est chargée par le chargeur de classe système (celui qui contient -classpath) qui n'a pas JUnit sur le chemin de classe. Ensuite, vous créez un chargeur de classe distinct qui n'a que JUnit sur le chemin d'accès aux classes. Lorsque votre classe principale tente de convertir en JUnitCore, le chargeur de classe système est invité à charger JUnitCore, qu'il ne contient pas, donc NoClassDefFoundError se produit.

Il n'y a aucun moyen pratique de faire ce que vous essayez de faire sans utiliser la réflexion. Vous devrez (1) créer une classe séparée qui accède directement à JUnitCore, (2) inclure un chemin vers cette classe sur URLClassLoader (répertoire ou JAR), (3) utiliser la réflexion pour charger cette classe, et (4) utiliser la réflexion pour invoque une méthode sur cette classe.

+0

J'avais peur de ceci: - /. Merci pour votre réponse bien formulée. –

-1

Ne vous devez soit faire .loadClass("org.junit.runner.JUnitCore", **true**); Ou bien invoquer resolveClass() de la classe objet avant de créer de nouvelles instances?

+0

Je me demande de quoi vous parlez, puisque Java 6 n'a ni un second argument sur ClassLoader.loadClass() ni une méthode Class.resolveClass(). –

+0

ClassLoader.loadClass possède un second argument booléen, mais la méthode n'est pas publique. ClassLoader.resolveClass existe. Cela dit, aucun n'est pertinent pour cette question, alors je suis d'accord avec le downvote. –