Haal de namen van klassen op in een JAR-bestand

1. Overzicht

De meeste Java-bibliotheken zijn beschikbaar als JAR-bestanden. In deze zelfstudie bespreken we hoe u namen van klassen in een bepaald JAR-bestand kunt krijgen vanaf de opdrachtregel en vanuit een Java-programma.

Vervolgens bekijken we een Java-programmavoorbeeld van het laden van klassen uit een bepaald JAR-bestand tijdens runtime.

2. Voorbeeld JAR-bestand

In deze tutorial nemen we de stripe-0.0.1-SNAPSHOT.jar bestand als voorbeeld om aan te geven hoe u de klassenamen in het JAR-bestand kunt krijgen:

3. Gebruik de pot Opdracht

JDK wordt geleverd met een pot opdracht. We kunnen dit commando gebruiken met de t en f opties om de inhoud van een JAR-bestand weer te geven:

$ jar tf stripe-0.0.1-SNAPSHOT.jar META-INF / META-INF / MANIFEST.MF ... sjablonen / resultaat.html sjablonen / checkout.html application.properties com / baeldung / stripe / StripeApplication.class com / baeldung / stripe / ChargeRequest.class com / baeldung / stripe / StripeService.class com / baeldung / stripe / ChargeRequest $ Currency.class ...

Omdat we alleen geïnteresseerd zijn in de *.klasse bestanden in het archief, kunnen we de uitvoer filteren met de grep opdracht:

$ jar tf stripe-0.0.1-SNAPSHOT.jar | grep '\ .class $' com / baeldung / stripe / StripeApplication.class com / baeldung / stripe / ChargeRequest.class com / baeldung / stripe / StripeService.class com / baeldung / stripe / ChargeRequest $ Currency.class com / baeldung / stripe /ChargeController.class com / baeldung / stripe / CheckoutController.class

Dit geeft ons een lijst met klassebestanden in het JAR-bestand.

4. Klasnamen van een JAR-bestand in Java ophalen

De ... gebruiken pot opdracht om de klassenamen uit een JAR-bestand af te drukken, is vrij eenvoudig. Soms willen we echter enkele klassen laden vanuit een JAR-bestand in ons Java-programma. In dit geval is de uitvoer van de opdrachtregel niet voldoende.

Om ons doel te bereiken, moeten we dat doen scan het JAR-bestand vanuit een Java-programma en krijg de klassenamen.

Laten we eens kijken hoe we klassenamen kunnen extraheren uit ons JAR-voorbeeldbestand de ... gebruiken JarFile en JarEntry klassen:

openbare statische Set getClassNamesFromJarFile (File givenFile) gooit IOException {Set classNames = new HashSet (); probeer (JarFile jarFile = nieuwe JarFile (gegevenFile)) {Opsomming e = jarFile.entries (); while (e.hasMoreElements ()) {JarEntry jarEntry = e.nextElement (); if (jarEntry.getName (). endsWith (". class")) {String className = jarEntry.getName () .replace ("/", ".") .replace (". class", ""); classNames.add (className); }} return classNames; }} 

Laten we nu de code in de bovenstaande methode eens nader bekijken en begrijpen hoe deze werkt:

  • probeer (JarFile jarFile = nieuwe JarFile (gegevenFile)) - Hier hebben we een 'try-with-resources'-instructie gebruikt om het jarFile van het gegeven het dossier voorwerp
  • if (jarEntry.getName (). endsWith (". class")) {…} - We nemen elke les jarEntry, en verander het pad van het klassenbestand in de gekwalificeerde klassenaam, bijvoorbeeld change "Pakket1 / pakket2 / SomeType.class" in "Package1.package2.SomeType"

Laten we controleren of de methode de klassennamen uit ons JAR-voorbeeldbestand kan extraheren via een unit-testmethode:

private static final String JAR_PATH = "voorbeeld-jar / stripe-0.0.1-SNAPSHOT.jar"; private statische laatste Set EXPECTED_CLASS_NAMES = Sets.newHashSet ("com.baeldung.stripe.StripeApplication", "com.baeldung.stripe.ChargeRequest", "com.baeldung.stripe.StripeService", "com.baeldung.stripe.ChargeRequest $ Currency "," com.baeldung.stripe.ChargeController "," com.baeldung.stripe.CheckoutController "); @Test openbare leegte gegevenJarFilePath_whenLoadClassNames_thenGetClassNames () gooit IOException, URISyntaxException {File jarFile = nieuw bestand (Objects.requireNonNull (getClass (). GetClassLoader (). GetResource (JAR_PATH))). Stel classNames = GetClassNamesFromJar.getClassNamesFromJarFile (jarFile); Assert.assertEquals (EXPECTED_CLASS_NAMES, classNames); } 

5. Klassen ophalen uit een JAR-bestand in Java

We hebben gezien hoe we de klassenamen uit een JAR-bestand kunnen halen. Soms willen we sommige klassen dynamisch laden uit een JAR-bestand tijdens runtime.

In dit geval kunnen we eerst de klassenamen uit het opgegeven JAR-bestand halen met onze getClassNamesFromJarFile methode.

Vervolgens kunnen we een ClassLoader om vereiste klassen op naam te laden:

openbare statische Set getClassesFromJarFile (bestand jarFile) gooit IOException, ClassNotFoundException {Set classNames = getClassNamesFromJarFile (jarFile); Set classes = new HashSet (classNames.size ()); probeer (URLClassLoader cl = URLClassLoader.newInstance (nieuwe URL [] {nieuwe URL ("jar: file:" + jarFile + "! /")}))) {voor (Stringnaam: classNames) {Class clazz = cl.loadClass ( naam); // Laad de klasse met zijn naam classes.add (clazz); }} terugkeer klassen; }

In de bovenstaande methode hebben we een URLClassLoader object om de klassen te laden. De implementatie is vrij eenvoudig.

Het is echter waarschijnlijk de moeite waard om de syntaxis voor de JAR-URL een beetje uit te leggen. Een geldige JAR-URL bestaat uit drie delen: "pot: + [de locatie van het JAR-bestand] + !/”.

De afsluitende '!/”Geeft aan dat de JAR-URL verwijst naar een volledig JAR-bestand. Laten we een paar voorbeelden van JAR-URL's bekijken:

jar: //www.example.com/some_jar_file.jar! / jar: file: /local/path/to/some_jar_file.jar! / jar: file: / C: /windows/path/to/some_jar_file.jar! /

In onze getClassesFromJarFile methode, bevindt het JAR-bestand zich op het lokale bestandssysteem, daarom is het voorvoegsel van de URL “het dossier:“.

Laten we nu een testmethode schrijven om te controleren of onze methode alles kan krijgen wat verwacht kan worden Klasse voorwerpen:

@Test openbare leegte gegevenJarFilePath_whenLoadClass_thenGetClassObjects () gooit IOException, ClassNotFoundException, URISyntaxException {File jarFile = nieuw bestand (Objects.requireNonNull (getClass (). GetClassLoader ()). GetResource (JAR). Stel klassen = GetClassNamesFromJar.getClassesFromJarFile (jarFile); Set names = classes.stream (). Map (Class :: getName) .collect (Collectors.toSet ()); Assert.assertEquals (EXPECTED_CLASS_NAMES, namen); } 

Zodra we de vereiste hebben Klasse objecten, kunnen we Java-reflectie gebruiken om instanties van klassen te maken en methoden aan te roepen.

6. Conclusie

In dit artikel hebben we twee verschillende benaderingen geleerd om klassenamen uit een bepaald JAR-bestand te halen.

De pot commando kan de klassenamen afdrukken. Het is best handig als we moeten controleren of een JAR-bestand een bepaalde klasse bevat. Als we echter de klassenamen moeten ophalen van een actief Java-programma, JarFile en JarEntry kan ons daarbij helpen.

Eindelijk hebben we ook een voorbeeld gezien van een Java-programma om tijdens runtime klassen uit een JAR-bestand te laden.

Zoals altijd is de volledige broncode van het artikel beschikbaar op GitHub.