Автор оригинала: Mohan Sundararaju.
1. Обзор
Платформа Java раньше была монолитной архитектурой, объединяющей все пакеты как единое целое.
В Java 9 это было упорядочено с введением Система модулей платформы Java (JPMS) , или Модули для краткости. Связанные пакеты были сгруппированы под модули, и модули заменили пакеты, чтобы стать основной единицей повторного .
В этом быстром учебнике мы пройдемся по некоторым вопросам, связанным с модулями, с которыми мы можем столкнуться при миграция существующего приложения на Java 9 .
2. Простой пример
Давайте рассмотрим простое приложение Java 8, которое содержит четыре метода, которые действительны под Java 8, но сложны в будущих версиях. Мы будем использовать эти методы, чтобы понять влияние миграции на Java 9.
Первый метод получает имя поставщика JCE ссылки в приложении:
private static void getCrytpographyProviderName() { LOGGER.info("1. JCE Provider Name: {}\n", new SunJCE().getName()); }
Второй метод перечисляет имена классов в стеке трассировки :
private static void getCallStackClassNames() { StringBuffer sbStack = new StringBuffer(); int i = 0; Class> caller = Reflection.getCallerClass(i++); do { sbStack.append(i + ".").append(caller.getName()) .append("\n"); caller = Reflection.getCallerClass(i++); } while (caller != null); LOGGER.info("2. Call Stack:\n{}", sbStack); }
Третий метод преобразует объект Java в XML :
private static void getXmlFromObject(Book book) throws JAXBException { Marshaller marshallerObj = JAXBContext.newInstance(Book.class).createMarshaller(); marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter sw = new StringWriter(); marshallerObj.marshal(book, sw); LOGGER.info("3. Xml for Book object:\n{}", sw); }
И окончательный метод кодирует строку на базу 64 с помощью sun.misc.BASE64Энкодер , из внутренних библиотек JDK :
private static void getBase64EncodedString(String inputString) { String encodedString = new BASE64Encoder().encode(inputString.getBytes()); LOGGER.info("4. Base Encoded String: {}", encodedString); }
Давайте вызовем все методы из основного метода:
public static void main(String[] args) throws Exception { getCrytpographyProviderName(); getCallStackClassNames(); getXmlFromObject(new Book(100, "Java Modules Architecture")); getBase64EncodedString("Java"); }
Когда мы запускаем это приложение в Java 8, мы получаем следующее:
> java -jar target\pre-jpms.jar [INFO] 1. JCE Provider Name: SunJCE [INFO] 2. Call Stack: 1.sun.reflect.Reflection 2.com.baeldung.prejpms.App 3.com.baeldung.prejpms.App [INFO] 3. Xml for Book object:[INFO] 4. Base Encoded String: SmF2YQ== Java Modules Architecture
Как правило, Java-версии гарантируют обратную совместимость, но JPMS изменения некоторых из этого.
3. Казнь на Java 9
Теперь давайте за запустите это приложение в Java 9:
>java -jar target\pre-jpms.jar [INFO] 1. JCE Provider Name: SunJCE [INFO] 2. Call Stack: 1.sun.reflect.Reflection 2.com.baeldung.prejpms.App 3.com.baeldung.prejpms.App [ERROR] java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext [ERROR] java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder
Мы видим, что первые два метода работают нормально, в то время как последние два не удалось. Давайте выясним причину сбоя, проанализировав зависимости наших приложений . Мы будем использовать jdeps инструмент, поставляемый с Java 9:
>jdeps target\pre-jpms.jar com.baeldung.prejpms -> com.sun.crypto.provider JDK internal API (java.base) com.baeldung.prejpms -> java.io java.base com.baeldung.prejpms -> java.lang java.base com.baeldung.prejpms -> javax.xml.bind java.xml.bind com.baeldung.prejpms -> javax.xml.bind.annotation java.xml.bind com.baeldung.prejpms -> org.slf4j not found com.baeldung.prejpms -> sun.misc JDK internal API (JDK removed internal API) com.baeldung.prejpms -> sun.reflect JDK internal API (jdk.unsupported)
Выход из команды дает:
- список всех пакетов внутри нашего приложения в первой колонке
- список всех зависимостей в нашем приложении во второй колонке
- расположение зависимостей на платформе Java 9 — это может быть имя модуля или внутренний API JDK, или нет для сторонних библиотек
4. Обеженные модули
Давайте попробуем решить первую ошибку java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext.
В соответствии со списком зависимостей, мы знаем, что Java.xml.bind пакет принадлежит Java.xml.bind модуль который, как представляется, является действительным модулем. Итак, давайте посмотрим на официальная документация для этого модуля .
В официальной документации говорится, что Java.xml.bind модуль уничает для удаления в будущем выпуске. Следовательно, этот модуль не загружается по умолчанию в classpath.
Тем не менее, Java предоставляет метод загрузки модулей по требованию с помощью – дополнительные модули выбор. Итак, давайте идти вперед и попробовать:
>java --add-modules java.xml.bind -jar target\pre-jpms.jar ... INFO 3. Xml for Book object:... Java Modules Architecture
Мы видим, что исполнение было успешным. Это решение является быстрым и простым, но не лучшим решением.
Как долгосрочное решение, мы должны добавить зависимость в качестве стороннёй библиотеки с использованием Maven:
javax.xml.bind jaxb-api 2.3.1
5. Внутренние API JDK
Давайте теперь рассмотрим вторую ошибку java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder.
Из списка зависимостей мы видим, что sun.misc пакет является Внутренняя JDK API.
Внутренние API, как следует из названия, являются частным кодом, используемым внутренне в JDK.
В нашем примере внутренний API, по-видимому, был удален из системы JDK . Давайте проверим, что альтернативный API для этого с помощью -jdk-внутренние выбор:
>jdeps --jdk-internals target\pre-jpms.jar ... JDK Internal API Suggested Replacement ---------------- --------------------- com.sun.crypto.provider.SunJCE Use java.security.Security.getProvider(provider-name) @since 1.3 sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8 sun.reflect.Reflection Use java.lang.StackWalker @since 9
Мы видим, что Java 9 рекомендует использовать java.util.Base64 вместо sun.misc.Base64Encoder. Следовательно, изменение кода является обязательным для запуска нашего приложения на Java 9.
Обратите внимание, что в нашем приложении есть два других внутренних API, для которых java-платформа предложила замену, но мы не получили никаких ошибок для них:
- Некоторые внутренние API, такие как sun.reflect.Отражение были сочтены критически важными для платформы и поэтому были добавлены к JDK-специфической jdk.unsupported модуль. Этот модуль доступен по умолчанию на classpath в Java 9.
- Внутренние API, такие как com.sun.crypto.provider.SunJCE предоставляются только на определенных реализациях Java. До тех пор, пока код, использующий их, работает на той же реализации, он не будет бросать какие-либо ошибки.
Во всех случаях в этом примере мы используем внутренние API, что не является рекомендуемой практикой . Таким образом, долгосрочное решение заключается в том, чтобы заменить их подходящими общественными API, предоставляемыми платформой.
6. Заключение
В этой статье мы увидели, как модульная система введена в Java 9 может вызвать проблемы с миграцией для некоторых старых приложений с помощью увяди или внутренних .
Мы также видели, как применять краткосрочные и долгосрочные исправления для этих ошибок.
Как всегда, примеры из этой статьи доступны более на GitHub .