ru:https://highload.today/blogs/kak-perenesti-proekt-s-java-11-na-java-17-razbiraem-samye-chastye-oshibki/ ua:https://highload.today/uk/blogs/yak-perenesti-proyekt-z-java-11-na-java-17-rozbirayemo-najchastishi-pomilki/
logo
Опыт      27/05/2022

Как перенести проект с Java 11 на Java 17: разбираем самые частые ошибки

Микита Земницький BLOG

Java Developer в NIX

Java 17 — новая LTS-версия — вышла на рынок почти год назад, но до сих пор не приобрела значительную популярность. О том, почему на нее следует перейти, я подробно рассказывал на конференции NIX MultiConf и в своей предыдущей статье на Highload.

Сегодня я опишу основные шаги и особенности миграции проекта с Java 11 в Java 17. Я буду пытаться рассмотреть возможные ошибки и варианты их решения.

Сразу подчеркиваю: я рассмотрю миграцию с программой Spring Boot. Это один из самых популярных фреймворков, которые используют большинство Java-разработчиков.

Правда, официальная поддержка Java 17 начинается с Spring 6 и Spring Boot 3, но эти версии еще в разработке. Последняя же версия Spring Boot — 2.6.7 . Ее и рассмотрим. Хотя она официально не поддерживается Java 17, разработчики Spring приложили много усилий, чтобы версии Spring Boot от 2.6.5 уже в определенной степени работали с новой LTS.

Как вообще мигрировать на любую Java-программу?

Для начала вам нужно в pom.xml найти properties и изменить версию с текущей на 17-ю. Это может выглядеть так:

<properties>
   <java.version>11</java.version>
</properties>

на 

<properties>
   <java.version>17</java.version>
</properties>

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

error: release version 17 not supported

Поначалу может быть непонятно, почему так произошло. Поэтому попытаемся пошагово разобраться с проблемой и решить ее.

Lombok

Самая первая ошибка — это Lombok. Так называется Java-библиотека, автоматизирующая генерацию шаблонного кода. Она может генерировать гетеры, сеттеры, конструкторы, логирование и т.д., освобождая классы от загромождения шаблонным кодом. При миграции с 11-й на 17-ю версии Java вы получаете следующую ошибку:

Онлайн-курс "Управління ІТ-командами" від Laba.
Прокачайте свої soft- і hard-скіли в управлінні кількома IT-командами, отримайте практичні стратегії та інструменти ефективного team-ліда.
Програма курсу і реєстрація
java.lang.IllegalAccesError: class lombok.javac.apt.LombokProcessor cannot access classcom.sun.tools.javac.proseccing.JavacProcessingEnvironment

Все это касается версии Lombok 1.18.22. На момент миграции моего приложения у меня была версия 1.18.12. Чтобы понять причину этой ошибки, нужно взглянуть на JEP 396, который добавил суровую инкапсуляцию внутренних компонентов JDK по умолчанию.

Этот JEP был реализован еще в 16 версии Java, и именно из-за него у Lombok больше нет возможности использовать внутренние компоненты JDK.

Сначала разработчики Lombok решили использовать следующую команду для отмены предыдущего JEP:

--illegal-access=permit

Но это было действительно только для Java 16. В 17-й же версии эту команду убрали. Поэтому в Lombok придумали другое решение. Вам следует добавить в maven-compiler-plugin аргументы. Таким образом вы даете доступ к нужным компонентам Lombok:

<compilerArgs>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
</compilerArgs>

Но вручную вносить аргументы не очень удобно. Поэтому в Lombok выпустили версию 1.18.22, в которой это делается автоматически — вам достаточно добавить эту зависимость. Для этого разработчики с помощью рефлексии изменили видимость модулей — и уже после этого у вас есть доступ.

Да, это не очень секьюрно, но все же работает.

MapStruct

MapStruct — процессор аннотаций Java для автоматического генерирования преобразователей (mapper) между Java-компонентами.

MapStruct использует сгенерированные гетеры, сеттеры и конструкторы для создания преобразователей. После обновления Lombok до версии 1.18.22 преобразователи больше не создаются именно из-за той ошибки — из-за того, что был реализован JEP 396, прибавивший строгую инкапсуляцию внутренних модулей.

Онлайн-курс "Директор з продажу" від Laba.
Як стратегічно впливати на дохід компанії, мотивувати сейлзів перевиконувати KPI та впроваджувати аналітику — навчить комерційний директор Laba з 12-річним досвідом у продажах.
Приєднатись до курсу

Чтобы обойти этот момент, необходимо добавить обработчик аннотаций lombok-mapstruct-bindingк maven-compiler-plugin:

<path>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-mapstruct-binding</artifactId>
    <version>0.2.0</version>
</path>

После этого все должно работать без проблем.

ASM

ASM — это среда для манипулирования байтовым кодом Java. ASM использует CGLIB, который, в свою очередь, используется Spring для AOP.

В Spring Framework AOP-прокси — это динамический прокси JDK или прокси CGLIB. Spring использует CGLIB и ASM, генерирует прокси-классы, несовместимые со средой выполнения Java 17. Spring Boot ниже 2.4 и зависит от Spring Framework 5.2, который использует версию CGLIB и ASM, несовместимую с Java 17.

Но обновление библиотек CGLIB или ASM невозможно, поскольку Spring переупаковывает ASM для внутреннего использования. То есть единственный выход из этой ситуации — обновить Spring Boot. Без этого ASM не заработает.

JUnit и отсутствующее свойство spring-boot.version

В новой версии Spring Boot spring-boot-dependenciesбыло удалено свойство spring-boot.version. Разработчики использовали ее для многих задач, скажем, чтобы исключать какую-либо зависимость.

Вот пример с исключением junit-vintage-engine:

<dependencyManagement>
  <dependencies>
     <dependecy>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <version>${spring-boot.version}</version>
         <exclusions>
            <exclusion>
              <groupId>org.junit.vintage</groupId>
              <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
         </exclusions>
     </dependecy>
  </dependencies>
</dependencyManagement>

К счастью, теперь мы можем удалить этот блок, поскольку Spring Boot 2.4 удалил Vintage Engine JUnit 5 со стартера spring-boot-starter-test.

Но если ваш проект до сих пор использует JUnit 4 и вы видите ошибки компиляции вроде java: package org.junit does not exist это потому что старый движок был удален.

Онлайн-курс Pyton від Powercode academy.
Опануйте PYTHON з нуля та майте проект у своєму портфоліо вже через 4 місяця.
Приєднатися

Старый движок отвечает за выполнение тестов JUnit 4 вместе с тестами JUnit 5. Если вы не можете из-за каких-либо обстоятельств перенести тесты на JUnit 5, добавьте в pom следующую зависимость:

<dependecy>
   <groupId>org.junit.vintage</groupId>
 <artifactId>junit-vintage-engine</artifactId>
   <scope>test</scope>
   <exclusions>
      <exclusion>
         <groupId>org.hamcrest</groupId>
         <artifactId>hamcrest-core</artifactId>
      </exclusion>
   </exclusions>
</dependecy>

Это полностью решит описанную проблему.

Jackson

Следующая возможная проблема связана с Jackson. Jackson — это библиотека инструментов обработки данных, например, для сериализации и десериализации JSON в компонентах Java и наоборот. Она может обрабатывать много форматов данных, но чаще всего ее используют для работы с JSON.

После обновления в Spring Boot 2.6.7 возникает следующая ошибка:

java.time.OffsetDateTime not supported by default: add Module “com.fasterxml.jackson.datatype:jackson-datatype-jsr310”

Причина — модуль JSR-310 недоступен для Jackson. Из-за смены Jackson 2.12 теперь это приводит к сбою сериализации, а не к тому, чтобы Jackson сериализировался в неожиданном формате.

Есть два решения этой проблемы. Вы можете создать mapper сами. Для версии 2.10 и более поздней через JsonMapper.buider это выглядит следующим образом:

ObjectMapper mapper = JsonMapper.buider()
               .addModule(new JavaTimeModule())
               .build();

Для более старой версии это можно сделать через mapper.registerModule:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

Но все это не очень хороший вариант, поскольку создание мапперов собственными руками требует лишнего времени и усилий. Поэтому предлагаю другой способ — попробовать автоконфигурацию Jackson.

Автоконфигурация Jackson

Spring Boot обеспечивает автоконфигурацию Jackson и автоматически объявляет полностью настроенный компонент ObjectMapper. Jackson можно настроить без определения собственного Bean-компонента ObjectMapper, используя свойства или класс Jackson2ObjectMapperBuilderCustomizer.

Онлайн-курс "Фінансовий аналіз" від Laba.
Навчіться читати фінзвітність так, щоб ухвалювати ефективні бізнес-рішення.Досвідом поділиться експерт, що 20 років займається фінансами і їхньою автоматизацією.
Детальніше про курс

Необходимо проверить, что модуль com.fasterxml.jackson.datatype:jackson-datatype-jsr310 находится в дороге к классам (т.е. в classpath) — и он будет автоматически зарегистрирован в ObjectMapper.

ObjectMapper потокобезопасен, поэтому его можно создать один раз и использовать повторно.

Валидатор запросов Swagger от Atlassian

Swagger от Atlassian представляет собой библиотеку для валидации запросов и ответов Swagger/OpenAPI 3.0. Старая версия библиотеки не использует автоконфигурацию Spring Boot Jackson и не регистрирует JavaTimeModule в своем ObjectMapper.

Но после обновления версии библиотеки тесты снова работают. Поэтому обязательно нужно использовать последнюю версию библиотек, обычно содержащую все необходимые исправления.

Вместо вывода

В общем, это наиболее заметные ошибки, с которыми я столкнулся при переходе на Java 17 и которые мне показались достойными внимания.

Конечно, на практике проблем может быть больше, но они в большинстве своем незначительны и требуют не столько особых подходов, сколько внимательности и дополнительного времени для решения. На мой взгляд, это все оправдывается преимуществами последней LTS-версии: она более функциональна и быстра.

Поэтому не бойтесь мигрировать свои проекты из Java 11 на Java 17. Это не так страшно, как может сначала казаться.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Основи Web дизайну від Ithillel.
Цей онлайн-курс з основ веб-дизайну дозволить вам опанувати мистецтво створення ефективних та привабливих інтерфейсів для вебсайтів і застосунків. Ви оволодієте ключовими принципами UX/UI дизайну, створюватимете дизайн-макети та прототипи, розроблятимете адаптивні інтерфейси для різних пристроїв, готуючись до професійної кар'єри в галузі веб-дизайну.
Дізнатися більше

Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.

Топ-5 самых популярных блогеров марта

PHP Developer в ScrumLaunch
Всего просмотровВсего просмотров
2434
#1
Всего просмотровВсего просмотров
2434
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсего просмотров
113
#2
Всего просмотровВсего просмотров
113
Career Consultant в GoIT
Всего просмотровВсего просмотров
95
#3
Всего просмотровВсего просмотров
95
CEO & Founder в Trustee
Всего просмотровВсего просмотров
94
#4
Всего просмотровВсего просмотров
94
Рейтинг блогеров

Ваша жалоба отправлена модератору

Сообщить об опечатке

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