GWT и SpringBoot — есть ли разумный способ справиться с конфликтующими зависимостями?

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

Однако во время разработки, когда вы захотите использовать сервер кода gwt...

Зависимости времени компиляции GWT необходимы во время выполнения (исходники + классы) для целей отладки и перекомпиляции модуля GWT.

В то же время вы можете захотеть, чтобы серверная часть поддерживалась чем-то вроде spring-boot 1.3.5.RELEASE.

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

<hibernate-validator.version>5.2.4.Final</hibernate-validator.version>

Что, конечно, очень хорошо. На самом деле, это одна из многих хороших черт весны.

Gwt, с другой стороны, см. ссылку ниже, http://www.gwtproject.org/doc/latest/DevGuideValidation.html

по-прежнему требует, чтобы вы использовали:

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.1.0.Final</version>
            <scope>runtime</scope>              
        </dependency>

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

Тогда действительно вы можете тривиально решить вышеуказанную проблему, структурировав свой проект следующим образом:

ROOT
--- Backend-api
--- Backend-Impl
--- Gwt-Front-end
---- War or stand alone Spring jar module to glue it all together

Где вы делаете конец Gwt-Front зависимым от внутренних API для некоторых служб RPC и DTO. Зависимости pom.xml, которыми вы можете по большей части управлять только в вашем интерфейсном модуле, независимо от того, какие зависимости у вас есть на бэкэнде. В конечном счете, вы создаете войну или исполняемый jar-файл с весенней загрузкой, который содержит ваш gwt-код в качестве статических ресурсов и содержит ваш внутренний код со всеми его зависимостями, а именно последнюю версию валидатора Hirbernate.

Однако, когда вы пытаетесь получить pom, который работает для целей разработки, насколько я вижу, вам приходится глобально управлять зависимостями, которые являются общими между внутренним и внешним уровнями в ROOT. pom.xml и понизить ваши зависимости до версии, требуемой gwt.

То есть в идеальном мировом сценарии. У вас есть ROOT pom.xml, просто объявляющий ваши модули и порядок их сборки. И вы получаете свой back-end-impl, чтобы иметь возможность заявить, что он хочет наследовать зависимости от spring-boot-starter pom.xml и т. д.

В отличие от идеального сценария, в конфигурации pom.xml, которая действительно помогает вам во время разработки... Что ж, вам, возможно, придется пересмотреть корневой файл pom.xml. И вы должны добавить управляемые зависимости к этому корневому файлу pom.xml, чтобы для всех этих распространенных конфликтующих зависимостей между вашим интерфейсом GWT и серверной частью Spring Boot вам всегда приходилось забивать версию GWT и ставить более раннюю версию. на месте.

Это гарантирует, что когда вы выполняете gwt:run, перекомпилируете в режиме разработки и т. д., компилятор gwt не будет пытаться скомпилировать javascript вашей Hibernate версии 5, а вместо этого будет использовать версию, которая действительно поддерживается финальной версией GWT 4.1. Конечно, вы можете столкнуться с сюрпризом в бэкэнд-коде, в один из дней внедрив такой хак...

Есть ли у кого-нибудь представление о том, как правильно организовать многомодульный проект, в котором серверная часть и интерфейс на основе gwt могут иметь конфликтующие требования к зависимостям?

В конечном счете, если ответ отрицательный, я полагаю, что предпочту иметь потери сети и дополнительную задержку связи, имея чистый автономный сервер загрузки spring, который интегрируется с автономным причалом только на чистом gwt, где сервер на этом причале делает не что иное, как тупо отбрасывает запросы к фактическому бэкенду spring-boot. Как-то жалко иметь серверную часть gwt jetty, которая вызывается для выполнения 1 + 1 пользовательским интерфейсом и перенаправляет вычисления на вторую серверную часть, выполняющую загрузку sprin, которая на самом деле знает, как выполнять 1 + 1 ... но если это самый продуктивный способ работы, гарантирующий, что разработка будет продуктивной, а производство будет проходить без неожиданностей, пусть будет так.

Спасибо за любой отзыв, и в идеале я хотел бы увидеть проект с несколькими помпонами, который вы можете импортировать как существующий проект maven в eclipse, и продемонстрировать, как этого можно достичь.


person 99Sono    schedule 17.05.2016    source источник
comment
Я не могу рассказать о Spring Boot, понятия не имею, как вы запускаете его в dev, но если вы ищете многомодульный проект GWT с Jetty или Tomcat, взгляните на мои архетипы на github.com/tbroyer/gwt-maven-archetypes   -  person Thomas Broyer    schedule 18.05.2016
comment
Просто как примечание. Я могу понять любого, кто любит Java как технологию разработки и хотел бы использовать GWT для пользовательского интерфейса. Но, откровенно говоря, я думаю, что для тех, кто начинает с нуля проект пользовательского интерфейса, нет другой лучшей альтернативы, чем TypeScript с Angular 4. Поэтому я действительно могу порекомендовать писать бэкэнд на java JEE/Springboot, а фронтенд ни чем иным, как Visual Code с Angular. 4 и Машинопись. Это лучшее, что есть на сегодняшний день, ИМХО. Я думаю, что GWT был прекрасным, но в конце концов Google выбрал что-то другое. Так что я не собираюсь использовать GWT в ближайшее время.   -  person 99Sono    schedule 16.09.2017


Ответы (3)


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

    <dependency>
        <groupId>spring-dependency-with-hibernate</groupId>
        <artifactId>some-spring-boot-starter</artifactId>
        <exclusions>
            <exclusion>
               <groupId>org.hibernate</groupId>
               <artifactId>hibernate-validator</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

Таким образом, во время выполнения будет доступна только версия hibernate, предоставленная GWT.

person Alex    schedule 18.05.2016
comment
Я считаю, что это не так прямолинейно. Я пытаюсь вспомнить, почему... Я не верю, что проблема заключалась в том, что компонент пользовательского интерфейса зависел от внутреннего компонента API, потому что компонент API почти не имеет транзитивных зависимостей. Туда попали только gwt-rpic api библиотеки. Я считаю, что основная трудность при жонглировании зависимостями заключалась в том, что для того, чтобы gwt:run обнаруживал изменения класса, нужно запускать встроенный контейнер spring из самого компонента пользовательского интерфейса. Если ваш код пользовательского интерфейса находится в подмодуле, изменения не подлежат горячей замене. - person 99Sono; 18.05.2016
comment
В противном случае компонент пользовательского интерфейса с кодом GWT вряд ли наследовал бы какие-либо транзитивные зависимости, завися только от backend-api, который не должен быть магнитом для зависимостей. Таким образом, проблема, скорее всего, заключалась в вынужденной необходимости запуска контейнера из проекта пользовательского интерфейса, чтобы заставить работать компиляцию источников пользовательского интерфейса с возможностью горячей замены, и это вынуждает вас иметь стартовое приложение SpingBoot, каким-то образом загрязняющее пространство зависимостей. Я должен буду просмотреть его. Но, возможно, я забыл подумать об игре с исключением зависимостей. Я отпишусь, когда буду уверен в магните зависимости. Спасибо. - person 99Sono; 18.05.2016
comment
Добавление плюса. Ваш совет может быть не серебряной пулей, которую я ищу, а выстрелом в правильном направлении. - person 99Sono; 18.05.2016

Да, то, что я хотел сделать, на самом деле можно сделать, хотя и не тривиально... совсем нет.

Чтобы настроить многомодульный pom maven, который можно продуктивно использовать в том смысле, что вы можете получить обмен кодом как для внутреннего, так и для внешнего кода без необходимости выбирать зависимости валидатора gwt или зависимости валидатора springboot, по сути, я организую мой проект следующим образом.

У вас есть:

LEVEL 1: myproject-root
----- LEVEL 2: myproject-backend
---------- LEVEL 3: myproject-backend-api
---------- LEVEL 3: myproject-backend-impl
------ LEVEL 2: myproject-frontend
------ LEVEL 2: myproject-runner
-----------LEVEL 3: myproject-runner-jar
-----------LEVEL 3: myproject-runner-war

В myproject-root вы, естественно, объявляете подмодули. Вы управляете зависимостями некоторых безобидных плагинов, таких как плагин surefire. Вы управляете исходным кодом Java и целевым языком компиляции. Вы делаете очень немного больше, чем это. Если у вас есть какие-то плагины, которые вы можете настраивать перекрестно, вы можете поместить их в управление плагинами.

В myproject-backend вы, по сути, наследуете зависимости Springboot от управления зависимостями, добавляя к управлению зависимостями:

<dependencyManagement>
        <dependencies>
            <!-- Inherit dependencies from spring boot dependency management pom -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${version.spring.boot}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Вы объявляете два подмодуля и уходите. Вы не объявляете здесь никаких перекрестных зависимостей. Вы используете только управление зависимостями. Интерфейсный компонент будет обращаться к подмодулю API, и вы не хотите создавать магнит зависимостей во внутреннем pom, поэтому здесь вы максимально консервативны.

В myproject-backend-api вы начинаете с того, что позволяете этому подмодулю быть подмодулем jar. В дальнейшем вы захотите разветвить API-компонент дальше, разделив API-интерфейсы по характеру точки входа. Начнем с того, что компонент -api будет иметь API, необходимый для связи между сервером и внешним интерфейсом, что-то вроде «общего» в документации. Здесь вы максимально осторожны, чтобы иметь как можно меньше элементов в элементе зависимостей. Внешний интерфейс будет использовать эти API, и вы не хотите, чтобы ваш пакет API зависел от каких-либо API весенней загрузки.

В backend-impl вы пишете свою фактическую бизнес-логику, и вы можете свободно зависеть от полного стека загрузки Spring. Правило таково: следите за тем, чтобы никто никогда не указывал на этот магнит зависимостей, особенно никогда не позволяйте компоненту gwt даже почувствовать запах существования библиотек Springboot. Не обращайтесь к нему никогда.

В myproject-frontend вы не разветвляете его на разные подмодули, как вы могли бы сделать с компонентом API. Здесь вы создаете монолитный компонент для кода gwt, чтобы вам было проще заставить плагин gwt maven работать на вас, выполняя перекомпиляцию горячего кода. В этом компоненте вы зависите от myproject-api, а также от любой соответствующей библиотеки gwt, такой как gwt-user, валидатор гибернации со старой версией, необходимой gwt, и т. д. Правило для myproject-frontend состоит в том, чтобы объявить каждая отдельная зависимость в компоненте как необязательная. Вы не хотите принимать какие-либо транзитивные зависимости от компонента. В производственной среде все, что вам нужно, это myproject-frontend.jar/META-INF/resources/MyProjectModule.html... И все такие статические ресурсы, которые Springboot должен будет обнаруживать и обслуживать. Однако пока вы находитесь в режиме разработки, то, что вы хотите сделать в этом внешнем компоненте, это: (а) настроить ваш gwt-плагин, чтобы он не запускал сервер причала

<noServer>true</noServer>

Дело в том, что если запустить этот сервер причала, единственное, что он будет иметь в своем пути к классам, будет код внешнего интерфейса и API, а определенно не код внедрения. И во-вторых, вы не хотите использовать причал gwt, вы либо хотите использовать tomcat springboot, либо причал springboot. Гораздо лучше использовать встроенные контейнеры Springboot с соответствующими библиотеками веб-сокетов и тому подобное.

(b) С помощью Springboot вы хотите серверировать свои статические ресурсы из META-INF/resources/ или общей папки. Я предпочитаю ресурсы, так как это было бы там, где вы обычно размещаете компоненты JSF. В любом случае, поскольку место, откуда берутся статические ресурсы, особенное, вы хотите настроить свой плагин gwt для компиляции исходных кодов Java в папку записи.

<webappDirectory>${project.build.directory}/classes/META-INF/resources</webappDirectory>

Итак, что-то вроде выше.

Наконец, и это было моей главной ошибкой, вы не запускаете свой код там, где вы пишете свой интерфейсный код. Это неправильно, потому что это заставляет ваш интерфейсный код нуждаться в зависимостях Springboot, что причинит вам сильную боль из-за конфликтующих библиотек.

Итак, чтобы избежать этой проблемы, вы создаете компонент myproject-runner, и если вы хотите быть очень подробным, вы делаете его родительским компонентом pom, чтобы вы могли разветвить свою упаковку либо на jar, либо на war. Лично мне исполняемый jar нравится гораздо больше, чем военный вариант. Я никогда не захочу развертывать приложение sprintboot в чем-то вроде тяжелой веб-логики ... но что бы ни раскачивало вашу лодку.

В любом случае, Spring Boot Runner — это ваша конечная зависимость magenet. Этот компонент будет зависеть от каждого модуля, который у вас есть, и от их транзитивных зависимостей. Но поскольку вы отделяете свой компонент бегуна от внешнего кода и делаете все зависимости во внешнем коде опциональными... Ну, по сути, вы связываете только springboot и ваши статические ресурсы gwt.

Пока вы развиваетесь, то, что вы делаете, это... как только вы это узнаете... просто. (A) вы запускаете приложение spring boot jar, запуская основной файл. Если вы действительно хотите развернуть военный файл, это ваш выбор, вы также можете создать военный файл и развернуть его на коте или что-то еще...

(b) вы переходите к компоненту внешнего интерфейса, щелкаете его правой кнопкой мыши, выбираете запуск как Maven Build и gwt:run. Запуск gwt запустит сервер кода gwt, который будет полностью слеп к вашему внутреннему коду, единственное, что он увидит перед глазами, это код, который есть у вас во внешнем компоненте, а также все ваши зависимости от gwt. добавлено как необязательное.

Наконец-то вы можете выполнять горячую замену кода в бэкэнде, если используете Springboot dev. Наконец-то вы можете «горячую» замену кода в этом монолитном внешнем компоненте, как только вы запустите плагин gwt.

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

person 99Sono    schedule 21.05.2016

Я полагаю, что у меня были похожие проблемы, и я обнаружил следующее при использовании Eclipse IDE, GWT Google Plugin, SpringBoot и Maven. Рецепт не простой, но он сработал для моей команды.

GWT-dev и сервер Jetty не работают с SpringBoot. Вы должны исправить путь к классам из-за жестко запрограммированных зависимостей Jetty и gwt-dev, будут работать только те зависимости, против которых были созданы gwt-dev и Jetty.

Мне пришлось разделить проект пользовательского интерфейса GWT на два артефакта Maven. Одним из артефактов было веб-приложение, способное работать только в Eclipse, при этом все библиотеки SpringBoot были исключены из пути к классам. Другим артефактом был развертываемый JAR-файл SpringBoot с типом упаковки maven «war» и наложением WAR на специальный артефакт Eclipse, включающий только специфический контент GWT.

надеюсь, это поможет

person upperlimit    schedule 04.01.2020