Перейти к основному содержимому

Получить имена классов внутри файла JAR

· 4 мин. чтения

Задача: Наибольшая подстрока палиндром

Для заданной строки s, верните наибольшую подстроку палиндром входящую в s. Подстрока — это непрерывная непустая последовательность символов внутри строки. Стока является палиндромом, если она читается одинаково в обоих направлениях...

ANDROMEDA 42

1. Обзор

Большинство библиотек Java доступны в виде файлов JAR . В этом руководстве мы рассмотрим, как получить имена классов внутри заданного файла JAR из командной строки и из программы Java.

Затем мы рассмотрим пример программы на Java, загружающей классы из заданного файла JAR во время выполнения.

2. Пример файла JAR

В этом руководстве мы возьмем файл stripe-0.0.1-SNAPSHOT.jar в качестве примера, чтобы узнать, как получить имена классов в файле JAR:

./a1157cef3bd41855504f435b9d998d27.png

3. Использование команды jar

JDK поставляется с командой jar . Мы можем использовать эту команду с параметрами t и f , чтобы просмотреть содержимое файла JAR :

$ jar tf stripe-0.0.1-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
...
templates/result.html
templates/checkout.html
application.properties
com/foreach/stripe/StripeApplication.class
com/foreach/stripe/ChargeRequest.class
com/foreach/stripe/StripeService.class
com/foreach/stripe/ChargeRequest$Currency.class
...

Поскольку нас интересуют только файлы *.class в архиве, мы можем отфильтровать вывод с помощью команды grep :

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

Это дает нам список файлов классов внутри файла JAR.

4. Получение имен классов файла JAR в Java

Использование команды jar для печати имен классов из файла JAR довольно просто. Однако иногда нам нужно загрузить некоторые классы из файла JAR в нашу программу Java. В этом случае вывода командной строки недостаточно.

Чтобы достичь нашей цели, нам нужно отсканировать файл JAR из программы Java и получить имена классов.

Давайте посмотрим, как извлечь имена классов из JAR-файла нашего примера, используя классы JarFile и JarEntry :

public static Set<String> getClassNamesFromJarFile(File givenFile) throws IOException {
Set<String> classNames = new HashSet<>();
try (JarFile jarFile = new JarFile(givenFile)) {
Enumeration<JarEntry> 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;
}
}

Теперь давайте подробнее рассмотрим код в приведенном выше методе и поймем, как он работает:

  • try (JarFile jarFile = new JarFile(givenFile)) — здесь мы использовали оператор try-with-resources, чтобы получить jarFile из заданного объекта File .
  • if (jarEntry.getName().endsWith(“.class”)){…} – мы берем каждый класс jarEntry и меняем путь к файлу класса на полное имя класса, например, изменяем «package1/package2/SomeType. класс» в «package1.package2.SomeType»

Давайте проверим, может ли метод извлечь имена классов из нашего примера JAR-файла с помощью метода модульного тестирования:

private static final String JAR_PATH = "example-jar/stripe-0.0.1-SNAPSHOT.jar";
private static final Set<String> EXPECTED_CLASS_NAMES = Sets.newHashSet(
"com.foreach.stripe.StripeApplication",
"com.foreach.stripe.ChargeRequest",
"com.foreach.stripe.StripeService",
"com.foreach.stripe.ChargeRequest$Currency",
"com.foreach.stripe.ChargeController",
"com.foreach.stripe.CheckoutController");

@Test
public void givenJarFilePath_whenLoadClassNames_thenGetClassNames() throws IOException, URISyntaxException {
File jarFile = new File(
Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());

Set<String> classNames = GetClassNamesFromJar.getClassNamesFromJarFile(jarFile);

Assert.assertEquals(EXPECTED_CLASS_NAMES, classNames);
}

5. Получение классов из файла JAR в Java

Мы видели, как получить имена классов из JAR-файла. Иногда нам нужно динамически загружать некоторые классы из JAR-файла во время выполнения.

В этом случае мы можем сначала получить имена классов из данного файла JAR, используя наш метод getClassNamesFromJarFile .

Затем мы можем создать ClassLoader для загрузки необходимых классов по имени:

public static Set<Class> getClassesFromJarFile(File jarFile) throws IOException, ClassNotFoundException {
Set<String> classNames = getClassNamesFromJarFile(jarFile);
Set<Class> classes = new HashSet<>(classNames.size());
try (URLClassLoader cl = URLClassLoader.newInstance(
new URL[] { new URL("jar:file:" + jarFile + "!/") })) {
for (String name : classNames) {
Class clazz = cl.loadClass(name); // Load the class by its name
classes.add(clazz);
}
}
return classes;
}

В приведенном выше методе мы создали объект URLClassLoader для загрузки классов. Реализация довольно проста.

Однако, вероятно, стоит немного объяснить синтаксис URL-адреса JAR. Действительный URL-адрес JAR состоит из трех частей: « jar: + [расположение файла JAR] + !/».

Завершающий « !/ » указывает, что URL-адрес JAR относится ко всему файлу JAR. Давайте посмотрим на несколько примеров URL JAR:

jar:http://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!/

В нашем методе getClassesFromJarFile файл JAR находится в локальной файловой системе, поэтому префикс URL-адреса — « file: ».

Теперь давайте напишем тестовый метод, чтобы проверить, может ли наш метод получить все ожидаемые объекты класса :

@Test
public void givenJarFilePath_whenLoadClass_thenGetClassObjects()
throws IOException, ClassNotFoundException, URISyntaxException {
File jarFile
= new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());
Set<Class> classes = GetClassNamesFromJar.getClassesFromJarFile(jarFile);
Set<String> names = classes.stream().map(Class::getName).collect(Collectors.toSet());
Assert.assertEquals(EXPECTED_CLASS_NAMES, names);
}

Когда у нас есть необходимые объекты класса , мы можем использовать отражение Java для создания экземпляров классов и вызова методов.

6. Заключение

В этой статье мы узнали о двух разных подходах к получению имен классов из заданного JAR-файла.

Команда jar может печатать имена классов. Это очень удобно, если нам нужно проверить, содержит ли файл JAR данный класс. Однако, если нам нужно получить имена классов из работающей Java-программы, JarFile и JarEntry могут помочь нам в этом.

Наконец, мы также увидели пример Java-программы для загрузки классов из JAR-файла во время выполнения.

Как всегда, полный исходный код статьи доступен на GitHub .