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 ви отримуєте наступну помилку:

Основи Python для школярів від Ithillel.
Відкрийте для вашої дитини захопливий світ програмування з нашим онлайн-курсом "Програмування Python для школярів". Ми вивчимо основи програмування на прикладі мови Python, надаючи зрозумілі пояснення та цікаві практичні завдання.
Зареєструватися
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, який додав сувору інкапсуляцію внутрішніх модулів. Щоб обійти цей момент, необхідно додати обробник анотацій lombok-mapstruct-binding до maven-compiler-plugin:

Онлайн-курс "Бренд-менеджмент" від Laba.
Розберіться в комплексному управлінні брендом: від його структури до комунікації з аудиторією.Дізнайтесь принципи побудови бренд-стратегії, проведення досліджень і пошуку свого споживача.
Детальніше про курс
<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то це тому, що старий рушій був видалений.

Старий рушій відповідає за виконання тестів JUnit 4 разом із тестами JUnit 5. Якщо ви не можете через якісь обставини перенести тести на JUnit 5, додайте до pom наступну залежність:

Курс UX/UI дизайнер сайтів і застосунків з Alice K.
Курс від практикуючої UI/UX дизайнерки, після якого ви знатимете все про UI/UX дизайн .
Реєстрація на курс
<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.

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

Курс QA Manual (Тестування ПЗ мануальне) від Powercode academy.
Навчіться знаходити помилки та контролювати якість сайтів та додатків.
Записатися на курс

ObjectMapper — потокобезпечний, тому його можна створити раз і використовувати повторно.

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

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

Але після оновлення версії бібліотеки тести знову працюють. Тому потрібно обов’язково використовувати останню версію бібліотек, яка зазвичай містить усі необхідні виправлення.

Замість висновку

Загалом це найбільш помітні помилки, з якими я зіткнувся при переході на Java 17 і які мені здалися вартими уваги. Звичайно, на практиці проблем може бути більше, але вони здебільшого незначні та потребують не стільки особливих підходів, скільки уважності і додаткового часу для вирішення. На мою думку, це все виправдовується перевагами останньої LTS-версії: вона більш функціональна та швидка.

Тому не бійтеся мігрувати свої проєкти з Java 11 на Java 17. Це не так страшно, як може спочатку здаватися.

Якщо ви знайшли помилку, будь ласка, виділіть фрагмент тексту та натисніть Ctrl+Enter.

Англійська для IT від Englishdom.
В межах курсу можна освоїти ключові ІТ-теми та почати без проблем говорити з іноземними колегами.
Дійзнайтеся більше

Цей матеріал – не редакційний, це – особиста думка його автора. Редакція може не поділяти цю думку.

Топ-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
Рейтинг блогерів

Найбільш обговорювані статті

Топ текстів

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

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

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