Spring Boot War
Несколько часов назад вышел релиз Spring-Boot-1.3.0 Появилась возможность создавать war архивы через утилиту spring следующей командой
Рабочее веб приложение может уместиться в 1 твит. Никакие конфигурационные файлы не требуются. Можно запустить(или собрать в jar,war) следующий код:
Не смотря на то, что это WAR, он всё так же является запускаемым.
Spring Boot умеет создавать запускаемые jar и war файлы благодаря проекту
spring-boot-loader.
По умолчанию, в java нет возможности загружать вложенные jar файлы. Этим занимаются загрузчики,
которых spring подкидывает в проект при сборке.
В манифест добавляется строка Main-Class: org.springframework.boot.loader.WarLauncher
(или JarLauncher).
В WarLauncher есть public static void main который занимается запуском нашего приложения.
Можно запускать как обычный jar:
Или на jetty:
Или на wildfly
Структура Jar
example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-com | +-mycompany | + project | +-YouClasses.class +-lib +-dependency1.jar +-dependency2.jar
Структура War
example.war | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-WEB-INF +-classes | +-com | +-mycompany | +-project | +-YouClasses.class +-lib | +-dependency1.jar | +-dependency2.jar +-lib-provided +-servlet-api.jar +-dependency3.jar
Основные отличия WAR от JAR, собранных spring-boot-cli
- Строка указывающая на главный класс в MANIFEST.MF (JarLauncher, WarLauncher)
- Для War нужен класс наследник SpringBootServletInitializer
- Структура (Layout) архивов отличается
- В случае с war, зависимость
tomcat
помещается отдельно от основных зависимостей (lib-provided)
Перепаковка из jar в war (bash)
Создадим класс, наследующийся от SpringBootServletInitializer (в той же директории, где script.groovy)
Выполним скрипт:
Для сравнения, соберем war утилитой spring-boot-cli
Сравним
Добавление загрузчиков осуществляется в классе
ArchiveCommand.java
, где в зависимости от Layout
, классы попадают либо в корень архива (jar), либо в WEB-INF/classes/
(war).
Поэтому классы PackagedSpringApplicationLauncher и SpringApplicationLauncher при сравнении,
находятся в разных директориях. Но в classpath они всёравно попадают.
Отличается только SpringApplicationWebApplicationInitializer . Вместо него мы добавили ru/d10xa/springwar/ServletInitializer
---unique in build/app.war WEB-INF/classes/ru/d10xa/springwar/ServletInitializer.class org/springframework/boot/cli/app/SpringApplicationLauncher.class org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.class ---unique in build/spring-cli-app.war WEB-INF/classes/org/springframework/boot/cli/app/SpringApplicationLauncher.class WEB-INF/classes/org/springframework/boot/cli/app/SpringApplicationWebApplicationInitializer.class WEB-INF/classes/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.class
spring war VS gradle build
Сборка с утилитой spring отличается от gradle наличием groovy в classpath + несколько вспомогательных классов (например, для тестов).
Классы и jar’ы которые были добавлены spring-boot-cli при сборке архива:
WEB-INF/classes/org/springframework/boot/cli/app/SpringApplicationLauncher.class WEB-INF/classes/org/springframework/boot/cli/app/SpringApplicationWebApplicationInitializer.class WEB-INF/classes/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.class WEB-INF/lib/groovy-2.4.4.jar WEB-INF/lib/groovy-templates-2.4.4.jar WEB-INF/lib/groovy-xml-2.4.4.jar org/springframework/boot/groovy/DelegateTestRunner.class org/springframework/boot/groovy/DependencyManagementBom.class org/springframework/boot/groovy/EnableDeviceResolver.class org/springframework/boot/groovy/EnableGroovyTemplates.class org/springframework/boot/groovy/GroovyTemplate.class
А зачем вообще нужно собирать war? Issue
на гитхабе, в котором просили добавить сборку war был открыт больше года
и пул реквесты ни кто не присылал, хотя пофиксить это можно довольно просто.
Я собирал war потому, что на OpenShift PaaS можно было создать себе контейнер
с “каким нибудь” application-сервером в несколько кликов и потом через scp
кидать war в папку автодеплой.
Но, как оказалось, от application-сервера я получил только проблемы.
Локально с встроенным в jar томкатом всё работало хорошо, но
application-сервер на openshift некорректно преобразовывал русские буквы в url.
Теперь spring boot приложения я собираю только в jar.
Почитать как запускать jar на openshift можно в спринговой документации