Иногда, пытаясь использовать в проекте Spring Boot, одновременно интерфейс командной строки Spring Shell и библиотеку миграции Mongock, разработчики сталкиваются с тем, что запуск миграций не происходит, хотя остальные функции приложения работают в штатном режиме. В частности, это может случиться в процессе выполнения одного из домашних заданий курса по Spring. Почему так происходит, мы и попробуем выяснить в текущей заметке.
Spring Shell
Для начала стоит разобраться, как запускается интерактивный режим Spring Shell. Известно как минимум три способа запуска своей логики в Spring(Boot). А именно:
- InitializingBean — у бинов, реализующих данный интерфейс, будет вызван метод afterPropertiesSet после того, как все свойства были установлены. В т. ч. зависимости;
- CommandLineRunner— это интерфейс Spring Boot с методом run, который автоматически вызывается фреймворком у всех компонентов, реализующих этот интерфейс, после загрузки контекста приложения. В сам метод run передаются аргументы командной строки в виде массива строк;
- ApplicationRunner — то же самое, что CommandLineRunner, но аргументы командной строки представлены в виде объекта ApplicationArguments.
Последние два пункта работают в Spring Boot и выглядят, как основные кандидаты на отправную точку изысканий.
Давайте попробуем начать с того, что проверим, не запускается ли шелл с помощью ApplicationRunner. Если в IDEA дважды нажать на Shift, выставить флаг "Include non-project items" и ввести в строку поиска ApplicationRunner, то можно увидеть, кто еще использует в названии это сочетание слов. Среди найденных классов есть InteractiveShellApplicationRunner, который, по сути, отвечает за запуск интерактивного режима шелл (что следует уже из его названия). Следует обратить внимание, что над данным классом висит @Order(0), а значит он будет запущен раньше, чем ApplicationRunner-ы с более низким приоритетом. Например, те из них, что не имеют над своим классом аннотации @Order, или имеют, но у нее указано значение больше 0 (значение по умолчанию Integer.MAX_VALUE).
Mongock v3
С шеллом вроде разобрались. Настала очередь Mongock-а. У него в третьей версии есть два класса-раннера. А именно: SpringMongock и SpringBootMongock. Первый для запуска миграций использует InitializingBean, второй — ApplicationRunner (этот момент описан в readme.md на странице проекта в гитхабе). Над классом SpringBootMongock не висит аннотации @Order, а значит при использовании в приложении Spring Shell до его запуска просто не дойдет дело. Стало быть, использование SpringMongock должно решить нашу проблему с запуском миграций. Просто объявляем бин нужного типа и все работает.
Mongock v4
В четвертой версии вся конфигурация скрыта от нас в недрах библиотеки. Мы можем настраивать работу с помощью свойств в application.yml и включать/отключать использование миграций аннотацией @EnableMongock. Если перейти в код данной аннотации, то можно увидеть, что она импортирует две конфигурации:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({MongockConfiguration.class, MongockContext.class})
public @interface EnableMongock {
}
Первая отвечает за загрузку свойств из application.yml, а вот MongockContext для нас будет гораздо интереснее. Вот его код:
@Configuration
@Import(MongockCoreContextSelector.class)
public class MongockContext {
@Bean
@ConditionalOnProperty(value = "mongock.runner-type", matchIfMissing = true, havingValue = "ApplicationRunner")
public MongockSpring5.MongockApplicationRunner mongockApplicationRunner(MongockSpring5.Builder mongockBuilder) {
return mongockBuilder.buildApplicationRunner();
}
@Bean
@ConditionalOnProperty(value = "mongock.runner-type", havingValue = "InitializingBean")
public MongockSpring5.MongockInitializingBeanRunner mongockInitializingBeanRunner(MongockSpring5.Builder mongockBuilder) {
return mongockBuilder.buildInitializingBeanRunner();
}
}
Т. е. все то же самое, что и в третьей версии. Два раннера, реализующие ApplicationRunner и InitializingBean. Какой из вариантов будет использоваться, зависит от значения свойства "mongock.runner-type". Если оно не указано, то будет создан раннер на основе ApplicationRunner. А значит, проблема интеграции Mongock и Spring Shell может быть решена указанием строки mongock.runner-type="InitializingBean" в настройках приложения.