Je voudrais obtenir une liste de toutes les classes appartenant à un certain paquet ainsi que de tous leurs enfants. Les classes peuvent ou non être déjà chargées dans la JVM.
Eh bien, ce que j'ai fait était simplement de lister tous les fichiers du classpath. Ce n'est peut-être pas une solution glorieuse, mais cela fonctionne de manière fiable et me donne tout ce que je veux et plus encore.
Ce n'est pas une solution programmatique mais vous pouvez exécuter
Java -verbose:class ....
et la machine virtuelle Java déchargera ce qu'elle charge et d'où.
[Opened /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded Java.lang.Object from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.io.Serializable from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.Comparable from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.CharSequence from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.String from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
Voir ici pour plus de détails.
en utilisant la bibliothèque Reflections , c’est simple:
Reflections reflections = new Reflections("my.pkg", new SubTypesScanner(false));
Cela permettrait d’analyser toutes les classes de l’url/s contenant le package my.pkg.
Donc, obtenir toutes les classes, c'est effectivement obtenir tous les sous-types d'Object, de manière transitoire:
Set<String> allClasses =
reflections.getStore().getSubTypesOf(Object.class.getName());
(La manière habituelle reflections.getSubTypesOf(Object.class)
provoquerait le chargement de toutes les classes dans PermGen et jetterait probablement OutOfMemoryError. Vous ne voulez pas le faire ...)
Si vous voulez obtenir tous les sous-types directs de Object (ou tout autre type), sans obtenir ses sous-types transitifs une fois, utilisez ceci:
Collection<String> directSubtypes =
reflections.getStore().get(SubTypesScanner.class).get(Object.class.getName());
Les réponses à cette question sont multiples, en partie à cause d'une question ambiguë - le titre parle de classes chargées par la machine virtuelle, alors que le contenu de la question indique "peut être chargé ou non par la machine virtuelle".
En supposant que OP a besoin des classes chargées par la JVM par un chargeur de classes donné, et uniquement de ces classes - mon besoin aussi - il existe une solution ( élaborée ici ) qui se présente comme suit:
import Java.net.URL;
import Java.util.Enumeration;
import Java.util.Iterator;
import Java.util.Vector;
public class CPTest {
private static Iterator list(ClassLoader CL)
throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Class CL_class = CL.getClass();
while (CL_class != Java.lang.ClassLoader.class) {
CL_class = CL_class.getSuperclass();
}
Java.lang.reflect.Field ClassLoader_classes_field = CL_class
.getDeclaredField("classes");
ClassLoader_classes_field.setAccessible(true);
Vector classes = (Vector) ClassLoader_classes_field.get(CL);
return classes.iterator();
}
public static void main(String args[]) throws Exception {
ClassLoader myCL = Thread.currentThread().getContextClassLoader();
while (myCL != null) {
System.out.println("ClassLoader: " + myCL);
for (Iterator iter = list(myCL); iter.hasNext();) {
System.out.println("\t" + iter.next());
}
myCL = myCL.getParent();
}
}
}
Une des choses intéressantes à ce sujet est que vous pouvez choisir un chargeur de classe arbitraire que vous souhaitez vérifier. Il est toutefois susceptible de casser si les éléments internes de la classe classloader changent, il doit donc être utilisé comme outil de diagnostic unique.
Je vous suggérerais également d'écrire un agent -javagent
, mais utilisez la méthode getAllLoadedClasses au lieu de transformer les classes.
Pour synchroniser avec votre code client (code Java normal), créez un socket et communiquez avec l'agent via celui-ci. Vous pouvez ensuite déclencher une méthode "list all class" à chaque fois que vous en avez besoin.
Une approche alternative à celles décrites ci-dessus serait de créer un agent externe à l'aide de Java.lang.instrument
pour savoir quelles classes sont chargées et d'exécuter votre programme avec le commutateur -javaagent
:
import Java.lang.instrument.ClassFileTransformer;
import Java.lang.instrument.IllegalClassFormatException;
import Java.security.ProtectionDomain;
public class SimpleTransformer implements ClassFileTransformer {
public SimpleTransformer() {
super();
}
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
System.out.println("Loading class: " + className);
return bytes;
}
}
Cette approche présente l’avantage supplémentaire de vous fournir des informations sur le chargeur de classe chargé dans une classe donnée.
Si vous connaissez déjà le chemin de niveau supérieur du paquet, vous pouvez utiliser OpenPojo
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
Ensuite, vous pouvez parcourir la liste et exécuter toutes les fonctionnalités souhaitées.
Exécutez votre code sous une JVM JRockit, puis utilisez JRCMD <PID> print_class_summary
Cela affichera toutes les classes chargées, une sur chaque ligne.
Vous pourrez peut-être obtenir une liste des classes chargées via le chargeur de classes, mais cela n'inclut pas les classes que vous n'avez pas encore chargées mais qui se trouvent sur votre chemin de classe.
Pour obtenir TOUTES les classes sur votre chemin de classe, vous devez faire quelque chose comme votre deuxième solution. Si vous voulez vraiment des classes qui sont actuellement "chargées" (en d’autres termes, des classes que vous avez déjà référencées, consultées ou instanciées), vous devez préciser votre question.
Depuis Oracle doc vous pouvez utiliser l’option -Xlog
qui permet d’écrire dans un fichier.
Java -Xlog:class+load=info:classloaded.txt
Ce programme imprimera toutes les classes avec son chemin physique. use peut simplement copier ceci dans n’importe quelle JSP si vous devez analyser le chargement de la classe à partir d’un serveur Web/d’application.
import Java.lang.reflect.Field;
import Java.util.Vector;
public class TestMain {
public static void main(String[] args) {
Field f;
try {
f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes = (Vector<Class>) f.get(classLoader);
for(Class cls : classes){
Java.net.URL location = cls.getResource('/' + cls.getName().replace('.',
'/') + ".class");
System.out.println("<p>"+location +"<p/>");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}