Руководство по миграции со Spring Boot 1.5 на 2.2.x

Привет!

Я Норберт и работаю в RSQ Technologies разработчиком программного обеспечения. Одним из наших основных продуктов является RSQ Health - система, которая поддерживает клиники, больницы и врачей в их повседневной работе с онлайн-документацией, расписаниями и базой данных пациентов. Мы также работаем над искусственным интеллектом и оборудованием, но это не тема этой статьи.

Начнем с предыстории проекта.

Наше основное приложение представляет собой монолит. Также есть несколько вспомогательных микросервисов.

Как вы могли догадаться - да, эта статья про это ядро.

Spring Boot 1.5.6 была последней версией, когда мы начали писать наш монолит в 2017 году. С тех пор прошло много времени - за это время было выпущено множество новых версий Spring Boot.

Проходили месяцы, и мы добавляли новые функции, исправления ошибок и т.д.… Никого не волновала устаревшая Spring Boot, пока я не подсчитал, что некоторые задачи, связанные с обновлением Hibernate, займут у меня (примерно) 1 час. Я узнал, что это связано с устаревшей зависимостью. Но… копая глубже, я заметил, что это проблема не зависимости, а всего устаревшего Spring Boot. Вот и я решил начать разговор об обновлении. В нашей компании на одной из наших ретроспективных / плановых еженедельных встреч я убедил своих товарищей по команде, что пришло время и мы должны это сделать! Для нас и для всеобщего блага! (Я сказал, что это может занять более 1 дня)

Наконец, весь процесс миграции занял у меня 2 недели, включая запросы на вытягивание и исправления.

Хочу поделиться с вами своим опытом.

Почему?

Позвольте мне объяснить, почему вам также следует проверять / обновлять зависимости и обновлять их.

  1. « Пора прощаться »
    В официальном сообщении блога Spring от 09–06–2019 говорится, что это конец 1 .x жизнь версии.
  2. Совместимость
    У нас есть несколько других служб, работающих с Spring Boot 2.x. Для связи мы используем клиент Feign, у которого были проблемы с совместимостью между такими разными версиями.
  3. Производительность
    Раньше сборки Jenkins занимали около 1,5 часа, а после обновления - 0,5 часа.
  4. Новые функции
    Последние функции и возможность обновления до Java 11.
  5. Счастливые товарищи по команде

Давайте начнем

Через месяц я посмотрел историю git и все сделанные коммиты. К счастью, во время миграции я держал в уме идею этой статьи.

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

Шаги

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

  • Изменения импорта
    Многие операции импорта изменились из-за зависимостей. Я не буду перечислять вам все зависимости от нашего проекта. Вы можете легко найти матрицу совместимости для каждого. Например Весеннее облако. Ваша IDE покажет вам все проблемы с импортом / зависимостями во время компиляции.
  • Свойства приложения
    Зависимость переносчика свойств действительно полезна при переносе свойств приложения (например, application.yml). Он найдет и произведет для вас все временные замены. Кроме того, он будет печатать вам предупреждение о каждом изменении.
    Если вы хотите сделать это самостоятельно - не стесняйтесь! Вот список всех свойств, использованных в Spring.
  • Переименование функций JPA
    Следующие методы CrudRepository были переименованы:
save → saveAll
delete → deleteAll
  • CrudRepository findOne
    Предыдущий пункт говорит вам только о замене имени метода репозитория. Однако есть еще одно изменение, на которое требуется больше времени. Функция репозитория findOne была заменена на findById и теперь возвращает необязательную. Заменять его во всем проекте может быть слишком запутанно, поэтому я создал расширение ниже:

Вы также можете использовать orElseThrow и генерировать исключение вместо возврата null.

  • HttpServletRequestMapper использует атрибуты вместо URI.
    Мы используем сопоставитель запросов в процессе управления версиями для поиска версий данной конечной точки. .
  • Индикаторы работоспособности при запуске
    Честно говоря, я не заметил этого в Spring Boot 1.5.4, но согласно документации он уже был там. 😕
    Если вы используете пружину. mail MailHealthIndicator проверит соединение при запуске. Вы можете отключить его в свойствах, установив: management.health.mail.enabled=false
  • Для абстрактного класса необходимо открыть val / var
    Классы, которые расширяют или переопределяют обычные параметры абстрактного класса, работать не будут. Параметры абстрактного класса должны быть open.
  • LocalDate / Time в сущностях требует columnDefinition
    Наличие LocalDate / Time в вашей сущности может быть проблематичным для создателя схемы. Мне пришлось добавить columnDefinitions к каждому столбцу сущности типа, как показано ниже:
java.time.LocalDate → @Column(columnDefinition=DATE)
java.time.LocalTime → @Column(columnDefinition=TIME)
  • Кодировка по умолчанию
    У меня возникли проблемы с кодировкой JSON в интеграционных тестах. В ответе были неправильно закодированные символы. Не уверен, в чем была причина, но я нашел решение. Я добавил кодировку по умолчанию в наш JacksonConfig.
  • Изменение формата JSON объекта страницы
    После миграции объект страницы, используемый как структура JSON, изменился. Я понял это, когда один из тестов не прошел. После некоторого исследования я добавил эту конструкцию в наш CustomPageImpl:

Полезные ссылки: StackOverflow, запись в блоге.

  • Переопределение бина
    В случае наличия нескольких бинов с одинаковым именем на основе активного профиля добавление allow-bean-definition-overriding: true помогло
  • Gradle BootRepackage переименован
    Согласно этой статье, bootRepackage был заменен на bootJarи bootWar.
  • Таблица истории схемы по умолчанию для Flyway переименована
    Версия Flyway изменена с 3.x → 6.x.
    Поэтому вам нужно будет переименовать имя таблицы по умолчанию в свойствах приложения. Flyway 6.x использует flyway_schema_history в качестве таблицы по умолчанию. Подробно это описано в StackOverflow.
flyway:
   table: "schema_version" 

Другое решение - переименовать таблицу прямо в базе данных.

  • Интеграция и модульное тестирование
    Наличие около 2k тестов, написанных с помощью JUnit 4, и их перенос на JUnit 5, по-видимому, занимает много времени. Зависимость от JUnit Vintage оказалась полезной. Он предоставляет движок для запуска тестов JUnit 4.
    Если вы хотите перейти на Junit 5, вам могут быть полезны JUnit docs или эта статья.
  • Очистка зависимостей
    При выполнении ./gradlew dependencies отображается дерево зависимостей и возможные обновления. После выполнения этой задачи я обнаружил некоторые зависимости, которые мы импортировали отдельно перед миграцией, которые теперь включены в другую зависимость или просто дублируются.
    Например:
    После обновления у нас было две зависимости hamcrest.
    И это результат выполнения задачи gradle:

Поэтому я просто удалил старую.

Резюме

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

  • Ознакомьтесь с официальной статьей о миграции Spring Boot и воспользуйтесь мигратором свойств, который ускорит первые шаги.
  • Держите свой мерзавец в чистоте. Работа над отдельной веткой. Мастер слияния, чтобы избежать конфликтов. Опишите изменения в коммитах. Думаю, без этого я бы сегодня не смог написать эту статью.
  • Если у вас есть другие службы, зависящие друг от друга, будьте осторожны с совместимостью зависимостей, например. Весеннее облако.
  • Своевременно обновляйте зависимости. ./gradlew dependencies помогут вам найти обновления.

Надеюсь, вы потратите меньше времени, чем я.