Hallo. Ich habe immer einige Probleme mit den obigen Lösungen (und auf anderen Seiten) gehabt.
Ich, als Entwickler, programmiere ein Addon für eine API. Die API verhindert die Verwendung von externen Bibliotheken oder Tools von Drittanbietern. Das Setup besteht außerdem aus einer Mischung aus Code in Jar- oder Zip-Dateien und Klassendateien, die sich direkt in einigen Verzeichnissen befinden. Mein Code musste also in der Lage sein, jedes Setup zu umgehen. Nach umfangreichen Recherchen habe ich eine Methode gefunden, die in mindestens 95 % aller möglichen Konfigurationen funktionieren wird.
Der folgende Code ist im Grunde die Overkill-Methode, die immer funktionieren wird.
Der Code:
Dieser Code durchsucht ein bestimmtes Paket nach allen Klassen, die darin enthalten sind. Er funktioniert nur für alle Klassen im aktuellen ClassLoader
.
/**
* Private helper method
*
* @param directory
* The directory to start with
* @param pckgname
* The package name to search for. Will be needed for getting the
* Class object.
* @param classes
* if a file isn't loaded but still is in the directory
* @throws ClassNotFoundException
*/
private static void checkDirectory(File directory, String pckgname,
ArrayList<Class<?>> classes) throws ClassNotFoundException {
File tmpDirectory;
if (directory.exists() && directory.isDirectory()) {
final String[] files = directory.list();
for (final String file : files) {
if (file.endsWith(".class")) {
try {
classes.add(Class.forName(pckgname + '.'
+ file.substring(0, file.length() - 6)));
} catch (final NoClassDefFoundError e) {
// do nothing. this class hasn't been found by the
// loader, and we don't care.
}
} else if ((tmpDirectory = new File(directory, file))
.isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes);
}
}
}
}
/**
* Private helper method.
*
* @param connection
* the connection to the jar
* @param pckgname
* the package name to search for
* @param classes
* the current ArrayList of all classes. This method will simply
* add new classes.
* @throws ClassNotFoundException
* if a file isn't loaded but still is in the jar file
* @throws IOException
* if it can't correctly read from the jar file.
*/
private static void checkJarFile(JarURLConnection connection,
String pckgname, ArrayList<Class<?>> classes)
throws ClassNotFoundException, IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> entries = jarFile.entries();
String name;
for (JarEntry jarEntry = null; entries.hasMoreElements()
&& ((jarEntry = entries.nextElement()) != null);) {
name = jarEntry.getName();
if (name.contains(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (name.contains(pckgname)) {
classes.add(Class.forName(name));
}
}
}
}
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
throws ClassNotFoundException {
final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
try {
final ClassLoader cld = Thread.currentThread()
.getContextClassLoader();
if (cld == null)
throw new ClassNotFoundException("Can't get class loader.");
final Enumeration<URL> resources = cld.getResources(pckgname
.replace('.', '/'));
URLConnection connection;
for (URL url = null; resources.hasMoreElements()
&& ((url = resources.nextElement()) != null);) {
try {
connection = url.openConnection();
if (connection instanceof JarURLConnection) {
checkJarFile((JarURLConnection) connection, pckgname,
classes);
} else if (connection instanceof FileURLConnection) {
try {
checkDirectory(
new File(URLDecoder.decode(url.getPath(),
"UTF-8")), pckgname, classes);
} catch (final UnsupportedEncodingException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Unsupported encoding)",
ex);
}
} else
throw new ClassNotFoundException(pckgname + " ("
+ url.getPath()
+ ") does not appear to be a valid package");
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
}
} catch (final NullPointerException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Null pointer exception)",
ex);
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
return classes;
}
Diese drei Methoden bieten Ihnen die Möglichkeit, alle Klassen in einem bestimmten Paket zu finden.
Sie verwenden es so:
getClassesForPackage("package.your.classes.are.in");
Die Erklärung:
Die Methode ermittelt zunächst die aktuelle ClassLoader
. Anschließend werden alle Ressourcen abgerufen, die dieses Paket enthalten, und diese durchlaufen URL
s. Anschließend wird ein URLConnection
und bestimmt, welche Art von URl wir haben. Es kann entweder ein Verzeichnis sein ( FileURLConnection
) oder ein Verzeichnis innerhalb einer jar- oder zip-Datei ( JarURLConnection
). Je nach Art der Verbindung werden zwei verschiedene Methoden aufgerufen.
Zunächst wollen wir sehen, was passiert, wenn es sich um eine FileURLConnection
.
Es wird zunächst geprüft, ob die übergebene Datei existiert und ein Verzeichnis ist. Wenn dies der Fall ist, wird geprüft, ob es sich um eine Klassendatei handelt. Wenn ja, wird eine Class
Objekt wird erstellt und in die ArrayList
. Wenn es sich nicht um eine Klassendatei, sondern um ein Verzeichnis handelt, wird es einfach durchlaufen und das Gleiche getan. Alle anderen Fälle/Dateien werden ignoriert.
Wenn die URLConnection
ist eine JarURLConnection
wird die andere private Hilfsmethode aufgerufen. Diese Methode iteriert über alle Einträge im zip/jar-Archiv. Wenn ein Eintrag eine Klassendatei ist und sich innerhalb des Pakets ein Class
Objekt wird erstellt und in der Datei ArrayList
.
Nachdem alle Ressourcen geparst worden sind, gibt sie (die Hauptmethode) die ArrayList
die alle Klassen im angegebenen Paket enthält, die der aktuelle ClassLoader
weiß Bescheid.
Wenn der Prozess an irgendeiner Stelle fehlschlägt, wird ein ClassNotFoundException
mit detaillierten Informationen über die genaue Ursache ausgelöst.
2 Stimmen
FYI die Lösung Amit Links zu funktioniert, obwohl es einen Fehler hat, wenn der Klassenpfad ein Leerzeichen in es hat (und wahrscheinlich für andere nicht-alphanumerische Zeichen zu). wenn Sie es in jeder Art von Produktionscode verwenden, siehe mein Kommentar zu seiner Antwort für eine Problemumgehung.
2 Stimmen
Beachten Sie auch diese Stelle .
1 Stimmen
Siehe dazugehörige Antwort: stackoverflow.com/a/30149061/4102160
1 Stimmen
Beachten Sie auch diese Stelle .
2 Stimmen
Siehe meine Antwort unten über ClassGraph, es ist derzeit die robusteste Methode zum Scannen der Klassenpfad und Modulpfad.
0 Stimmen
Dies ist ein sehr schwieriges Problem, vor allem wenn man bedenkt, dass nicht alle Classloader müssen dateibasiert sein (ich habe gesehen, wie sie Klassen aus der Datenbank laden) Ihre beste Wette ist wahrscheinlich die Kompilierzeit Annotation Verarbeitung verwenden, um eine Datei von Klassen zur Laufzeit zu verarbeiten. Die meisten Leute, die denken, dass es einfach ist, iterieren über Klassendateien auf Ihrem Klassenpfad - das ist nicht ein großer Ansatz im Allgemeinen, da, wie ich sagte, nicht alle Classloader auf den Klassenpfad beschränkt sind.