Проблема с загрузкой Spring Boot Jar

Сталкивались ли вы с проблемой запуска нового загрузочного архива Spring Boot?
Вообще, новация в этом направлении уже не первая, стандартов особых нет. Поэтому многие огребают проблемы и решают их на форумах и стек-оверфлоу.
Если вы также столкнулись с проблемой, я помогу её решить. В таком случае читаем дальше.
Итак, проблем с BootJar на самом деле хватает. Особенно учитывая, что уже минимум три версии формата поменялось.
Здесь я расскажу о часто встречающемся случае с потерей ресурсов. Конкретнее в моём случае — с потерей JSP шаблонов. Почему JSP? Мне так привычнее, проект я по-быстрому начал с них, и не думал, что будут такие проблемы.
Итак структура проекта (стандартный веб):
/src/main/ java/ resources/ static/ some.html public/ webapp/ WEB-INF/jsp index.jsp
По заявлению создателей BootJar / BootWar, jsp не поддерживается толком в новом BootJar формате. Но совместимость должна быть в BootWar. На это я и надеялся, когда ваял код. Пока ваял, никаких проблем — всё запускается, всё работает, как обычно, в общем. BootRun отрабатывает, только опции успевай подставлять.
Проблема пришла, откуда не ждали: когда пришло время разворачиваться на Амазоне, тогда и вылезла.
Итак, дубль раз. Чуть ли не в первый раз запускаю задачу BootJar. Ярхив готов, деплоим… Готово! Сигнала нет, сыпет ошибки 302 + 404 (это авторизация не находит вью). Но это пока не понятно.
Отключаем Секурити — всё равно ничего не находит, кроме голимой статики, и то не всей, а только из webjars. ???
Дубль два. Догадываясь о несовместимости jsp и BootJar, пакуем BootWar. Деплоим… Не работает. Не помню точно, но примерно то же самое в результате.
Хм, странно. Запускаем BootJar локально — всё работает. Чудеса.
Выяснилось: Spring Boot слишком умный, он при запуске из каталога разработки даже релизного ярника всё равно все тащит из каталога разработки. Из другого запускаем — перестаёт работать. Фух! Ну хоть отлаживать можно.
Что и делам. Выясняется — ресурсы BootJar запакованные не из библиотек (webjars), а из проекта, не включаются в перечисление, и это, оказывается, по дизайну! Подробности здесь.
Вернее не так — есть спец-ресурсы в каталогах типа static/, public/. И они вроде находятся, если объявить. Но jsp не видит в упор хоть тресни. И дело не в том, что не там лежат. Оказалось, что Томкат (в нашем плохом случае), грузит jsp особым механизмом после редиректа. И сами jsp можно загрузить без рендеринга, если правильно задать их положение в настройках
spring.resources.static-locations
Но это нас не устраивает.
Оказалось, что при использования встроенного томката, ресурсы вью он грузит отдельно и в первую очередь своей старой встроенной логикой, которую Спрингисты настраивать не научились. А этой логике нужен либо архив Вар, либо он же распакованный (почему кстати при разработке нормально отрабатывает расположение webapp/), либо ресурсы из библиотек, которые прекрасно видны, если правильно упакованы в изначальных либах — нужно чтоб лежали в META-INF/resources, как в стандарте сервлетов. Последнее работает даже внутри BootJar. Удивительно.
Почему не сработал распакованный архив? Ё-маё, на амазоне он распакуется черти-куда, и приложение про это место ничего не знает, если не сказать. Но хардкодить пути — плохая привычка. Сам ярник запускается затем безо всякой распаковки, хотя вроде права позволяют распаковать прям на месте. Ну да ладно, способ пролетел.
Почему не работает способ с Вар-архивом? Ё-маё, Амазон решил, что лучше меня знает, что я буду грузить именно в яр-формате, хотя интерфейс заявлет о готовности съесть варник. Он этот варник переименовывает в ярник, умник такой. А Томкат не умничает, он смотрит расширение: не Вар — ну тогда, извините, это не мой случай.
В итоге корень деплоя не находится. И ресурсы оттуда тоже. Ресурсы из статики не грузятся, потому что ищутся в корне и ресурсных либах, а не в classpath.
Ладно, проблема понятна. Решение?
Было три варианта.
- Сделать свой spring-загрузчик ресурсов. Вариант отпал, поскольку, как я уже сказал, Томкат отрабатывает jsp до их запуска.
- Прокинуть загрузку в Томкат. Стал прикидывать: надо расширить контекст spring-а, прокинуть пути в контекст Томката, там ещё раза два переложить — непонятно, насколько сложно, и можно ли без изменения самого томката. Спрингисты не осилили, и я не хочу.
- Вариант попроще — пакуем ресурсы в ресурсную либу в BootJar.
Вот о нём подробнее. Пихать всё в отдельный модуль, как предлагали здесь, не хотелось.
Делаем отдельной задачей в Gradle.
Создаём конфигурацию.
sourceSets { jsp { resources.source(sourceSets.main.resources); resources.srcDirs += ['src/main/webapp']; } jmh { .. .. } }
Сама задача
task jsp(type: Jar, description: 'JSP Packaging') { archiveBaseName = 'jsp' group = "build" def art = sourceSets.jsp.output from(art) { exclude('META-INF') into('META-INF/resources/') } from(art) { include('META-INF/*') into('/') } dependsOn(processJspResources) }
Задача processJspResources уже создана, её не надо делать. Ставим всё в зависимость и пакуем:
bootJar { dependsOn(jsp) bootInf.with { from(jsp.archiveFile) { include('**/*.jar') } into('lib/') } }
Как добавить другим способом (прямым), не нашёл — подключить в зависимости конфиг jspImplementation самого же проекта — нельзя, а хотелось бы. Но если все же из другого модуля забирать, то вот так ещё делаем:
artifacts { jspImplementation jsp }
Всё, теперь имеем ресурсную либу, которую по всем стандартам томкат должен грузить, и он грузит. Запускаем, как BootJar.
ссылка на оригинал статьи https://habr.com/ru/post/535246/
Добавить комментарий