Рубрики
Без рубрики

Файлы Jar с несколькими выпусками

Узнайте о файлах JAR с несколькими выпусками Java 9, используя простой пример

Автор оригинала: baeldung.

1. Обзор

Java постоянно развивается и добавляет новые функции в JDK. И, если мы хотим использовать эти функции в наших API, это может обязать нижестоящие зависимости обновить свою версию JDK.

Иногда мы вынуждены ждать использования новых языковых функций , чтобы оставаться совместимыми.

Однако в этом уроке мы узнаем о многоразрядных JAR (MR JAR) и о том, как они могут одновременно содержать реализации, совместимые с различными версиями JDK.

2. Простой Пример

Давайте взглянем на служебный класс под названием DateHelper , который имеет метод для проверки високосных лет . Предположим, что он был написан с использованием JDK 7 и построен для работы на JRE 7+:

public class DateHelper {
    public static boolean checkIfLeapYear(String dateStr) throws Exception {
        logger.info("Checking for leap year using Java 1 calendar API ");

        Calendar cal = Calendar.getInstance();
        cal.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(dateStr));
        int year = cal.get(Calendar.YEAR);

        return (new GregorianCalendar()).isLeapYear(year);
    }
}

Проверьте, будет ли метод Високосного года вызван из метода main нашего тестового приложения:

public class App {
    public static void main(String[] args) throws Exception {
        String dateToCheck = args[0];
        boolean isLeapYear = DateHelper.checkIfLeapYear(dateToCheck);
        logger.info("Date given " + dateToCheck + " is leap year: " + isLeapYear);
    }
}

Давайте перенесемся на сегодняшний день.

Мы знаем, что в Java 8 есть более краткий способ анализа даты . Итак, мы хотели бы воспользоваться этим и переписать вашу логику. Для этого нам нужно переключиться на JDK 8+. Однако это означало бы, что наш модуль перестанет работать на JRE 7, для которого он был первоначально написан.

И мы не хотим, чтобы это происходило без крайней необходимости.

3. Файлы Jar с несколькими выпусками

Решение в Java 9 состоит в том, чтобы оставить исходный класс нетронутым и вместо этого создать новую версию с использованием нового JDK и упаковать их вместе . Во время выполнения JVM (версия 9 или выше) вызовет любую из этих двух версий , отдавая предпочтение самой высокой версии, которую поддерживает JVM .

Например, если MRJAR содержит Java версии 7 (по умолчанию), 9 и 10 одного и того же класса, то JVM 10+ выполнит версию 10, а JVM 9 выполнит версию 9. В обоих случаях версия по умолчанию не выполняется, поскольку для этой JVM существует более подходящая версия.

Обратите внимание, что общедоступные определения новой версии класса должны точно соответствовать исходной версии . Другими словами, нам не разрешается добавлять какие-либо новые общедоступные API, исключительные для новой версии.

4. Структура папок

Поскольку классы в Java сопоставляются непосредственно с файлами по их именам, создание новой версии DateHelper в том же месте невозможно. Следовательно, нам нужно создать их в отдельной папке.

Давайте начнем с создания папки java 9 на том же уровне, что и java . После этого давайте клонируем DateHelper.java файл, сохраняющий структуру папок пакета, и поместите его в java 9:

src/
    main/
        java/
            com/
                baeldung/
                    multireleaseapp/
                        App.java
                        DateHelper.java
        java9/
            com/
                baeldung/
                    multireleaseapp/
                        DateHelper.java

Некоторые идеи, которые еще не поддерживают MRJARs , могут выдавать ошибки для дублирования DateHelper.java классы.

Мы рассмотрим как интегрировать это с инструментами сборки, такими как Maven , в другом уроке. А пока давайте просто сосредоточимся на основных принципах.

5. Изменения кода

Давайте перепишем логику java9 клонированного класса:

public class DateHelper {
    public static boolean checkIfLeapYear(String dateStr) throws Exception {
        logger.info("Checking for leap year using Java 9 Date Api");
        return LocalDate.parse(dateStr).isLeapYear();
    }
}

Обратите внимание, что мы не вносим никаких изменений в сигнатуры общедоступных методов клонированного класса, а только меняем внутреннюю логику. В то же время мы не добавляем никаких новых общедоступных методов.

Это очень важно, потому что создание jar завершится неудачей, если эти два правила не будут соблюдены.

6. Кросс-компиляция на Java

Кросс-компиляция-это функция в Java, которая может компилировать файлы для запуска в более ранних версиях. Это означает, что нам не нужно устанавливать отдельные версии JDK.

Давайте скомпилируем наши классы с помощью JDK 9 или выше.

Во – первых, скомпилируйте старый код для платформы Java 7:

javac --release 7 -d classes src\main\java\com\baeldung\multireleaseapp\*.java

Во-вторых, скомпилируйте новый код для платформы Java 9:

javac --release 9 -d classes-9 src\main\java9\com\baeldung\multireleaseapp\*.java

Параметр release используется для указания версии компилятора Java и целевой JRE.

7. Создание MR JAR

Наконец, создайте файл MR JAR с помощью версии 9+:

jar --create --file target/mrjar.jar --main-class com.baeldung.multireleaseapp.App
  -C classes . --release 9 -C classes-9 .

То освобождать опция, за которой следует имя папки, делает содержимое этой папки упакованным в файл jar под значением номера версии:

com/
    baeldung/
        multireleaseapp/
            App.class
            DateHelper.class
META-INF/
    versions/
        9/
            com/
                baeldung/
                    multireleaseapp/
                        DateHelper.class
    MANIFEST.MF

Файл MANIFEST.MF имеет свойство, позволяющее JVM знать, что это файл MR JAR:

Multi-Release: true

Следовательно, JVM загружает соответствующий класс во время выполнения.

Старые JVM игнорируют новое свойство, указывающее, что это файл MR JAR, и обрабатывают его как обычный файл JAR.

8. Тестирование

Наконец, давайте протестируем наш jar на Java 7 или 8:

> java -jar target/mrjar.jar "2012-09-22"
Checking for leap year using Java 1 calendar API 
Date given 2012-09-22 is leap year: true

А затем давайте снова протестируем jar на Java 9 или более поздней версии:

> java -jar target/mrjar.jar "2012-09-22"
Checking for leap year using Java 9 Date Api
Date given 2012-09-22 is leap year: true

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

В этой статье мы рассмотрели, как создать файл jar с несколькими выпусками, используя простой пример.

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