GWT и SpringBoot - есть ли умный способ справиться с конфликтующими зависимостями?
В идеальном мире ваше приложение GWT скомпилировано в javascript, вы используете его в качестве статического ресурса, а за кулисами у вас работает серверный код, работающий на JVM, и жизнь идет хорошо. Но этот идеальный мир называется производством во время выполнения.
Однако во время разработки, когда вы хотели бы использовать сервер кода gwt...
Вы GWT зависимости времени компиляции необходимы во время выполнения (исходники + классы), для целей отладки и перекомпиляции модуля GWT.
В то же время вы можете захотеть, чтобы back-end поддерживался чем-то вроде spring-boot 1.3.5.RELEASE.
В этом случае Spring Boot, страдающий от нескольких частых выпусков, в данный момент хочет добавить в качестве управляемой зависимости, например:
<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 end, зависит от внутренних API-интерфейсов, для некоторых служб RPC и DTO. Зависимости pom.xml, которыми вы в основном можете управлять только в своем внешнем модуле, независимо от того, какие зависимости у вас есть в бэкэнде. В конечном счете, вы создаете войну или исполняемый файл с пружинным загрузчиком, который переносит ваш код gwt в качестве статических ресурсов и переносит ваш внутренний код со всеми его зависимостями, а именно последнюю версию валидатора Hirbernate.
Однако, когда вы пытаетесь получить pom, который работает в целях разработки, насколько я вижу, вы застреваете в глобальном масштабе, чтобы управлять зависимостями, которые являются общими для внутреннего и внешнего уровня в ROOT. pom.xml и понижение ваших зависимостей до версии, требуемой gwt.
То есть в идеальном мире сценарий. У вас есть ваш ROOT pom.xml, который просто объявляет ваши модули и порядок их сборки. И вы получите свой back-end-impl, который может заявить, что он хочет наследовать зависимости от spring-boot-starter pom.xml и т. Д.
В отличие от идеального сценария, в конфигурации pom.xml, которая действительно помогает вам во время разработки... Ну, вам, возможно, придется пересмотреть Root pom.xml. И вы должны добавить управляемые зависимости к этому корневому pom.xml, чтобы для всех этих общих противоречивых зависимостей между вашим GWT-интерфейсом и бэк-эндом с весенней загрузкой вам всегда приходилось вбивать версию GWT и ставить понижение. на месте.
Это гарантирует, что при выполнении gwt:run, перекомпиляции режима dev и т. Д. Вы не получите компилятор gwt, пытающийся javascript скомпилировать вашу версию Hibernate 5, а вместо этого версию, которая действительно поддерживается GWT 4.1 final. Конечно, у вас может возникнуть сюрприз в бэкэнд-коде, на днях предложив такой взлом...
Кто-нибудь имеет представление о том, как правильно организовать многомодульный проект, где бэк-энду и gwt-интерфейсу разрешено иметь конфликтующие требования зависимости?
В конечном счете, если ответ "нет", я полагаю, что я предпочту иметь потерю сети и дополнительную задержку связи, имея чистый автономный бэкэнд с пружинной загрузкой, который интегрируется с чистым отдельно стоящим причалом gwt, где серверная часть этого причала не делает ничего, кроме как безмолвно пинает запросы к самому весеннему загрузчику. Это немного жалко иметь gwt jetty back-end, который вызывается для выполнения 1+1 пользовательским интерфейсом и который перенаправляет вычисления на вторую внутреннюю загрузочную sprin-загрузку, которая фактически знает, как сделать 1+1... но если это самый продуктивный способ работы, обеспечение того, чтобы развитие было продуктивным, а производство работало без сюрпризов, пусть будет так.
Спасибо за любые отзывы, и в идеале я хотел бы увидеть проект с несколькими помпами, который вы можете импортировать как существующий проект maven в eclipse, и демонстрирующий, как этого можно достичь.
4 ответа
Да, то, что я хотел сделать, на самом деле можно сделать, хотя и не тривиально... совсем нет.
Чтобы настроить много модульный 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, и вы не хотите создавать магнит зависимости в бэкэнд-модуле, так что здесь вы максимально консервативны.
В myproject-backend-api вы начинаете, позволяя этому подмодулю быть субмодулем jar. В дальнейшем вам нужно будет разветвлять компонент api, подразделяя api-ы по типу точки входа. Начнем с того, что компонент -api будет иметь apis, необходимый для связи между сервером и внешним интерфейсом, что-то вроде того, что обычно встречается в документации "совместно используемой". Здесь вы максимально осторожны, чтобы иметь как можно меньше информации об элементах в элементе зависимостей. Внешний интерфейс будет использовать эти API, и вы не хотите, чтобы ваш пакет API в зависимости от любого весеннего загрузочного API.
В backend-impl вы пишете реальную бизнес-логику и можете полностью зависеть от стека полной загрузки. Правило состоит в том, чтобы никто никогда не указывал на этот магнит зависимости, особенно, никогда не позволяйте компоненту gwt даже чувствовать запах библиотек Springboot. Не отсылай это никогда.
В интерфейсе myproject вы не разветвляете его на разные подмодули, как вы могли бы сделать с помощью компонента api. Здесь вы создаете монолитный компонент для кода gwt, чтобы вам было проще работать с плагином gwt maven, выполняющим горячую перекомпиляцию кода. В этом компоненте вы зависите от myproject-api, а также от любой соответствующей библиотеки gwt, такой как gwt-user, hibernate validator со старой версией, необходимой gwt, и т. Д. Правило для myproject-frontend заключается в объявлении каждая зависимость в компоненте необязательна. Вы не хотите принимать какую-либо транзитивную зависимость от компонента. В производственной среде все, что вам нужно, это myproject-frontend.jar/META-INF/resources/MyProjectModule.html... И все такие статические ресурсы, которые Springboot должен будет обнаруживать и обслуживать. Однако, пока вы находитесь в режиме разработки, вы хотите сделать следующее: (a) настроить плагин gwt, чтобы не запускать сервер Jetty
<noServer>true</noServer>
Суть в том, что если запустить этот Jetty-сервер, единственное, что у него будет в его classpath, - это код переднего плана и API, и определенно не подразумеваемый код. И, во-вторых, вы не хотите использовать причал GWT, вы либо хотите использовать Springboot Tomcat или Springboot Jetty. Гораздо лучше использовать встроенные контейнеры Springboot с соответствующими библиотеками веб-сокетов и тому подобное.
(b) С помощью Springboot вы хотите сервировать ваши статические ресурсы из META-INF / resources / или из общей папки. Я предпочитаю ресурсы, которые были бы там, где вы обычно размещаете компоненты JSF. В любом случае, поскольку место, откуда берутся статические ресурсы, является особенным, вы хотите настроить свой плагин gwt для компиляции ваших java-источников в папку записи.
<webappDirectory>${project.build.directory}/classes/META-INF/resources</webappDirectory>
Так что как выше.
Наконец, и это была моя главная ошибка: вы не запускаете свой код с того места, где вы пишете свой код переднего плана. Это неправильно, потому что это заставляет ваш код переднего плана нуждаться в зависимостях при загрузке, что причинит вам чрезвычайную боль из-за конфликтующих библиотек.
Поэтому, чтобы избежать этой проблемы, вы создаете компонент myproject-runner, и если вы хотите быть очень подробным, вы делаете его родительским компонентом pom, чтобы вы могли превратить вашу упаковку в jar или war. Мне лично нравится исполняемый фляга, гораздо более конкретный, чем вариант войны. Я никогда не захочу развернуть приложение sprinboot в чем-то вроде тяжелого weblogic... но что бы ни качало вашу лодку.
В любом случае, бегун с пружинными сапогами - ваша окончательная зависимость. Этот компонент будет зависеть от каждого вашего модуля и их транзитивных зависимостей. Но поскольку вы отделяете свой компонент runner от внешнего кода и делаете все зависимости в внешнем коде опциональными... Ну, вы, по сути, только связываете Springboot, и ваши статические ресурсы GWT.
Пока вы разрабатываете, то, что вы делаете, это... как только вы это знаете... просто. (A) вы запускаете приложение весенней загрузки, запуская основной файл. Если вы действительно хотите развернуть файл войны, вы можете также создать файл войны и развернуть его на томате или в любом другом месте...
(б) вы переходите к своему компоненту веб-интерфейса, щелкаете по нему правой кнопкой мыши, выбираете "запустить" от имени Maven Build и запускаете gwt: run. При запуске gwt запустится сервер кода gwt, который будет полностью скрыт от вашего внутреннего кода, единственное, что он увидит перед глазами, - это код, который вы имеете в компоненте внешнего интерфейса, а также все зависимости gwt, которые вы ' мы добавили в качестве optinal.
Наконец, вы можете выполнить горячую замену кода в бэкэнде, если вы используете Springboot Dev. Наконец, вы можете выполнить горячую замену кода в этом монолитном компоненте внешнего интерфейса, как только вы запустите плагин gwt.
Заключение, это возможно, но это адский беспорядок. Рад, что эта проблема не в пути.
Выясните, где транзитивная зависимость, которую вы хотите исключить при компиляции кода 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>
Таким образом, только GWT при условии hibernate
версия будет доступна во время выполнения.
Я считаю, что у меня были похожие проблемы, и я обнаружил следующее при использовании Eclipse IDE, GWT Google Plugin, SpringBoot и Maven. Рецепт непростой, но у моей команды он сработал.
Сервер GWT-dev и Jetty не работает с SpringBoot. Вам нужно исправить путь к классам из-за жестко запрограммированных зависимостей Jetty и gwt-dev, работать будут только зависимости gwt-dev и Jetty.
Мне пришлось разделить проект пользовательского интерфейса GWT на два артефакта Maven. Одним из артефактов было WebApp, способное работать только в Eclipse, при этом все библиотеки SpringBoot были исключены из пути к классам. Другим артефактом был развертываемый SpringBoot JAR с типом упаковки maven "war" и наложением WAR на выделенный артефакт Eclipse, включая только специфический контент GWT.
надеюсь, это поможет
Просто создайте специальный профиль maven для запуска кодового сервера GWT и добавьте ТОЛЬКО туда зависимость 'gwt-dev'
<profiles>
<profile>
<id>gwt-dev</id>
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>${gwt.version}</version>
<executions>
<execution>
<id>run-codeserver</id>
<goals>
<goal>run-codeserver</goal>
</goals>
</execution>
</executions>
<configuration>
<sourceLevel>${java.version}</sourceLevel>
<module>kn.iopm.documentserver.IDocServer</module>
<extraJvmArgs>-Xmx1G -Xms512M -Xss1G -Dlog.root=${project.build.directory}/log
</extraJvmArgs>
<runTarget>index.html</runTarget>
<persistentunitcachedir>${project.build.directory}</persistentunitcachedir>
<webappDirectory>${project.build.outputDirectory}/public</webappDirectory>
<deploy>${project.build.directory}/gwt-deploy</deploy>
<codeServerWorkDir>${project.build.directory}/gwt</codeServerWorkDir>
<launcherDir>${project.build.outputDirectory}/static</launcherDir>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>