Wie lade ich Klassen zur Laufzeit aus einem Ordner oder JAR?

Lesezeit: 7 Minuten

Wie lade ich Klassen zur Laufzeit aus einem Ordner oder
Sam Stern

Ich versuche, ein Java-Tool zu erstellen, das die Struktur einer Java-Anwendung scannt und einige aussagekräftige Informationen liefert. Dazu muss ich in der Lage sein, alle .class-Dateien vom Projektspeicherort (JAR/WAR oder nur ein Ordner) zu scannen und Reflektion zu verwenden, um mehr über ihre Methoden zu erfahren. Dies erweist sich als nahezu unmöglich.

Ich kann viele auf URLClassloader basierende Lösungen finden, die es mir ermöglichen, bestimmte Klassen aus einem Verzeichnis/Archiv zu laden, aber keine, die es mir ermöglichen, Klassen zu laden, ohne Informationen über den Klassennamen oder die Paketstruktur zu haben.

EDIT: Ich glaube, ich habe das schlecht formuliert. Mein Problem ist nicht, dass ich nicht alle Klassendateien erhalten kann, ich kann das mit Rekursion usw. tun und sie richtig lokalisieren. Mein Problem besteht darin, ein Klassenobjekt für jede Klassendatei zu erhalten.

  • Wie können Sie eine Klasse laden, die ihren vollständig qualifizierten Namen ignoriert? Ich glaube nicht, dass dies möglich ist.

    – Edwin Dalorzo

    13. Juni 12 um 13:59 Uhr

  • Mit den folgenden Antworten können Sie die Klassendateien finden, aber je nach Größe Ihres Projekts kann es sich lohnen, sie zu verwenden asm oder javassist. Damit können Sie die Klassen analysieren, ohne sie alle in den Speicher zu laden.

    – Nick Wilson

    13. Juni 12 um 14:04 Uhr

1644316745 442 Wie lade ich Klassen zur Laufzeit aus einem Ordner oder
Apfelsaft

Der folgende Code lädt alle Klassen aus einer JAR-Datei. Es muss nichts über die Klassen wissen. Die Namen der Klassen werden aus dem JarEntry extrahiert.

JarFile jarFile = new JarFile(pathToJar);
Enumeration<JarEntry> e = jarFile.entries();

URL[] urls = { new URL("jar:file:" + pathToJar+"!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);

while (e.hasMoreElements()) {
    JarEntry je = e.nextElement();
    if(je.isDirectory() || !je.getName().endsWith(".class")){
        continue;
    }
    // -6 because of .class
    String className = je.getName().substring(0,je.getName().length()-6);
    className = className.replace("https://stackoverflow.com/", '.');
    Class c = cl.loadClass(className);

}

bearbeiten:

Wie in den Kommentaren oben angedeutet, wäre javassist auch eine Möglichkeit. Initialisieren Sie einen ClassPool irgendwo vor der While-Schleife aus dem obigen Code, und anstatt die Klasse mit dem Klassenlader zu laden, könnten Sie ein CtClass-Objekt erstellen:

ClassPool cp = ClassPool.getDefault();
...
CtClass ctClass = cp.get(className);

Aus der ctClass können Sie alle Methoden, Felder, verschachtelten Klassen usw. abrufen. Schauen Sie sich die javassist-API an:
https://jboss-javassist.github.io/javassist/html/index.html

  • Entschuldigung, dass ich einen alten Beitrag hochhole (danke für diese Antwort übrigens, es funktioniert für mich). Jedoch, new URL("jar:file:" + pathToJar+"!/") verursacht a ClassNotFoundException. Was bei mir funktioniert hat war: URL[] urls = { new URL("jar:" + pathToJar+"!/") }; Nicht sicher warum? Hoffe, es hilft jemand anderem.

    – Taylorkresse

    6. Oktober 16 um 20:46 Uhr

  • Schön. Vielleicht möchten Sie die jarFile aber schließen? try { … } finally { jarFile.close(); }

    – Stefan Reich

    20. Mai ’17 um 11:10 Uhr

  • Der Link ist tot.

    – Kalle Richter

    18. Juli 17 um 20:35 Uhr

  • Link ist tot, jemand repariert es!

    – RayanFar

    30. August 17 um 21:20 Uhr

  • – String.valueOf(“.class”).length() könnte etwas ausdrucksstärker sein als “- 6”

    – Mann

    6. September 17 um 7:00 Uhr

Listen Sie alle Klassen in der JAR-Datei auf.

public static List getClasseNames(String jarName) {
    ArrayList classes = new ArrayList();

    if (debug)
        System.out.println("Jar " + jarName );
    try {
        JarInputStream jarFile = new JarInputStream(new FileInputStream(
                jarName));
        JarEntry jarEntry;

        while (true) {
            jarEntry = jarFile.getNextJarEntry();
            if (jarEntry == null) {
                break;
            }
            if (jarEntry.getName().endsWith(".class")) {
                if (debug)
                    System.out.println("Found "
                            + jarEntry.getName().replaceAll("https://stackoverflow.com/", "\."));
                classes.add(jarEntry.getName().replaceAll("https://stackoverflow.com/", "\."));
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return classes;
}

1644316746 592 Wie lade ich Klassen zur Laufzeit aus einem Ordner oder
Stefan C

Dazu muss ich in der Lage sein, alle .class-Dateien vom Projektspeicherort (JAR/WAR oder nur ein Ordner) zu scannen.

Das Scannen aller Dateien in einem Ordner ist einfach. Eine Möglichkeit ist anzurufen File.listFiles() auf der File das den Ordner bezeichnet, dann iterieren Sie das resultierende Array. Um Bäume verschachtelter Ordner zu durchlaufen, verwenden Sie Rekursion.

Das Scannen der Dateien einer JAR-Datei kann mit dem erfolgen JarFile API … und Sie müssen nicht rekursiv verschachtelte “Ordner” durchlaufen.

Beides ist nicht besonders kompliziert. Lesen Sie einfach das Javadoc und beginnen Sie mit dem Codieren.

1644316746 823 Wie lade ich Klassen zur Laufzeit aus einem Ordner oder
Sam Ginrich

Kam hier mit ähnlichen Anforderungen:

Haben Sie eine wachsende Anzahl von Dienstklassen in einem Paket, die eine gemeinsame Schnittstelle implementieren, und möchten Sie diese zur Laufzeit erkennen.

Ein Teilproblem besteht darin, Klassen in einem bestimmten Paket zu finden, wobei die Anwendung aus einer JAR-Datei oder aus entpackten Klassen in einer Paket-/Ordnerstruktur geladen werden kann.

Also habe ich die Lösungen von amicngh und an eingefügt anonym zusammen.

// Retrieve classes of a package and it's nested package from file based class repository

package esc;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class GetClasses
{
    private static boolean debug = false;
    
    /**
     * test function with assumed package esc.util
     */
    public static void main(String... args)
    {
        try
        {
            final Class<?>[] list = getClasses("esc.util");
            for (final Class<?> c : list)
            {
                System.out.println(c.getName());
            }
        }
        catch (final IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @precondition Thread Class loader attracts class and jar files, exclusively
     * @precondition Classes with static code sections are executed, when loaded and thus must not throw exceptions
     *
     * @param packageName
     *            [in] The base package path, dot-separated
     *
     * @return The classes of package /packageName/ and nested packages
     *
     * @throws IOException,
     *             ClassNotFoundException not applicable
     *
     * @author Sam Ginrich, http://www.java2s.com/example/java/reflection/recursive-method-used-to-find-all-classes-in-a-given-directory-and-sub.html
     *
     */
    public static Class<?>[] getClasses(String packageName) throws IOException
    {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        if (debug)
        {
            System.out.println("Class Loader class is " + classLoader.getClass().getName());
        }
        final String packagePath = packageName.replace('.', "https://stackoverflow.com/");
        final Enumeration<URL> resources = classLoader.getResources(packagePath);
        final List<Class<?>> classes = new ArrayList<Class<?>>();
        while (resources.hasMoreElements())
        {
            final URL resource = resources.nextElement();
            final String proto = resource.getProtocol();
            if ("file".equals(proto))
            {
                classes.addAll(findFileClasses(new File(resource.getFile()), packageName));
            }
            else if ("jar".equals(proto))
            {
                classes.addAll(findJarClasses(resource));
            }
            else
            {
                System.err.println("Protocol " + proto + " not supported");
                continue;
            }
        }
        return classes.toArray(new Class[classes.size()]);
    }

    
    /**
     * Linear search for classes of a package from a jar file
     *
     * @param packageResource
     *            [in] Jar URL of the base package, i.e. file URL bested in jar URL
     *
     * @return The classes of package /packageResource/ and nested packages
     *
     * @throws -
     *
     * @author amicngh, Sam [email protected]
     */
    private static List<Class<?>> findJarClasses(URL packageResource)
    {
        final List<Class<?>> classes = new ArrayList<Class<?>>();
        try
        {
            System.out.println("Jar URL Path is " + packageResource.getPath());
            final URL fileUrl = new URL(packageResource.getPath());
            final String proto = fileUrl.getProtocol();
            if ("file".equals(proto))
            {
                final String filePath = fileUrl.getPath().substring(1); // skip leading /
                final int jarTagPos = filePath.indexOf(".jar!/");
                if (jarTagPos < 0)
                {
                    System.err.println("Non-conformant jar file reference " + filePath + " !");
                }
                else
                {
                    final String packagePath = filePath.substring(jarTagPos + 6);
                    final String jarFilename = filePath.substring(0, jarTagPos + 4);
                    if (debug)
                    {
                        System.out.println("Package " + packagePath);
                        System.out.println("Jar file " + jarFilename);
                    }
                    final String packagePrefix = packagePath + "https://stackoverflow.com/";
                    try
                    {
                        final JarInputStream jarFile = new JarInputStream(
                                new FileInputStream(jarFilename));
                        JarEntry jarEntry;

                        while (true)
                        {
                            jarEntry = jarFile.getNextJarEntry();
                            if (jarEntry == null)
                            {
                                break;
                            }
                            final String classPath = jarEntry.getName();
                            if (classPath.startsWith(packagePrefix) && classPath.endsWith(".class"))
                            {
                                final String className = classPath
                                        .substring(0, classPath.length() - 6).replace("https://stackoverflow.com/", '.');

                                if (debug)
                                {
                                    System.out.println("Found entry " + jarEntry.getName());
                                }
                                try
                                {
                                    classes.add(Class.forName(className));
                                }
                                catch (final ClassNotFoundException x)
                                {
                                    System.err.println("Cannot load class " + className);
                                }
                            }
                        }
                        jarFile.close();
                    }
                    catch (final Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }
            else
            {
                System.err.println("Nested protocol " + proto + " not supprted!");
            }
        }
        catch (final MalformedURLException e)
        {
            e.printStackTrace();
        }
        return classes;
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory
     *            The base directory
     * @param packageName
     *            The package name for classes found inside the base directory
     * @return The classes
     * @author http://www.java2s.com/example/java/reflection/recursive-method-used-to-find-all-classes-in-a-given-directory-and-sub.html
     * @throws -
     *
     */
    private static List<Class<?>> findFileClasses(File directory, String packageName)
    {
        final List<Class<?>> classes = new ArrayList<Class<?>>();
        if (!directory.exists())
        {
            System.err.println("Directory " + directory.getAbsolutePath() + " does not exist.");
            return classes;
        }
        final File[] files = directory.listFiles();
        if (debug)
        {
            System.out.println("Directory "
                    + directory.getAbsolutePath()
                    + " has "
                    + files.length
                    + " elements.");
        }
        for (final File file : files)
        {
            if (file.isDirectory())
            {
                assert !file.getName().contains(".");
                classes.addAll(findFileClasses(file, packageName + "." + file.getName()));
            }
            else if (file.getName().endsWith(".class"))
            {
                final String className = packageName
                        + '.'
                        + file.getName().substring(0, file.getName().length() - 6);
                try
                {
                    classes.add(Class.forName(className));
                }
                catch (final ClassNotFoundException cnf)
                {
                    System.err.println("Cannot load class " + className);
                }
            }
        }
        return classes;
    }
}

.

821590cookie-checkWie lade ich Klassen zur Laufzeit aus einem Ordner oder JAR?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy