介绍
我最近一直在研究用Java编写UI代码的选项。 在我以前的文章中,我研究了Eclipse RAP,发现它可以与Spring Boot集成在一个可执行jar中。 这次我想对GWT做同样的技巧。
每个人都喜欢Spring Boot。 它使很多事情变得更加干净和容易。 但是从历史上看,用于在Web浏览器中创建UI的框架具有自己的方法来完成Spring Boot的某些工作。 不幸的是,在许多情况下,它们的方法看起来过时且过时。 因此,目标是尽可能使用Spring Boot并将GWT仅用于UI。
我必须警告读者,这篇文章实际上是TL; DR :-)的经典示例。
GWT方法
GWT使用特殊的编译器从Java代码生成Javascript代码。 该方法是创建一个模块描述文件.gwt.xml,用它导入其他模块,并使用GWT小部件用Java编写代码。 然后,他们的编译器将生成许多javascript代码,这些代码需要包含在html页面中。 他们在www.gwtproject.org上有一个讲解基本知识的教程 。
他们使用GWT RPC在后端调用方法。 这种方法需要在客户端和服务器之间共享一个接口。 客户端使用该接口来调用RPC方法。 该方法的服务器端实现在web.xml中注册为具有适当URL模式的Servlet。
我认为一个主要问题是调试。 最新版本的GWT采用了一种彻底的源地图方法。 这意味着Java代码调试在启用了源映射的浏览器中进行 ,而不是在Eclipse中进行(或者也许我无法使其在Eclipse中工作)。 我在Chrome浏览器中进行了尝试,它确实可以工作,但看起来有点麻烦。 默认情况下,GWT甚至不生成源映射。 为了使用它们,必须启动代码服务器并从此代码服务器在html页面中加载不同的javascript。 在这种情况下,大多数人都会向编译器添加一个选项。
我的意思是对GWT团队和这项技术的支持者没有冒犯,但总体而言,它看起来有些过时了。 他们不会花费太多时间来开发新功能。 甚至构建插件也由发烧友维护。
目标
这是我在调查中想要实现的目标:
- 仅将GWT用于生成将与其他所有内容一起归档到可执行jar中的Javascript代码。
- 将Spring Boot用于REST端点并完全避免GWT RPC
- 使用Spring Boot的可执行jar启动应用程序,并使用嵌入式Tomcat服务GWT html文件。 这也意味着可以使用所有其他出色的Spring Boot功能。
制作工具
为了实现目标1,我们需要一个好的构建工具。 我已经使用Maven插件从教程中创建了示例项目。 这是对我有用的完整配置:
<plugin>
<groupId>net.ltgt.gwt.maven</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>1.0-rc-6</version>
<executions>
<execution>
<goals>
<goal>import-sources</goal>
<goal>compile</goal>
<goal>import-test-sources</goal>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<moduleName>org.example.gwt.StockWatcher</moduleName>
<moduleShortName>StockWatcher</moduleShortName>
<failOnError>true</failOnError>
<!-- GWT compiler 2.8 requires 1.8, hence define sourceLevel here if you use
a different source language for java compilation -->
<sourceLevel>1.8</sourceLevel>
<!-- Compiler configuration -->
<compilerArgs>
<!-- Ask GWT to create the Story of Your Compile (SOYC) (gwt:compile) -->
<arg>-compileReport</arg>
<arg>-XcompilerMetrics</arg>
</compilerArgs>
<!-- DevMode configuration -->
<warDir>${project.build.directory}/${project.build.finalName}</warDir>
<classpathScope>compile+runtime</classpathScope>
<!-- URL(s) that should be opened by DevMode (gwt:devmode). -->
<startupUrls>
<startupUrl>StockWatcher.html</startupUrl>
</startupUrls>
</configuration>
</plugin>
使用GWT Eclipse插件,我可以使其工作,甚至调试也可以在Chrome中进行,因为Eclipse的GWT插件会自动启动代码服务器,并以某种方式更新html文件以从代码服务器加载javascript。
最重要的是:GWT Maven插件有效:-))。 但是,将Spring Boot和GWT集成起来将是一项复杂的任务。 我需要先运行GWT编译,然后将结果javascript添加到可执行文件Jar中。 也许可以用Maven做到这一点,但是对于这个任务,我决定使用Gradle 。
Gradle是一种快速发展的构建工具。 DSL和API尚不稳定,但提供了很大的灵活性。 虽然Maven的构建阶段相当直线,但是Gradle可以按任何顺序执行任务。 我需要这种灵活性。
经过一番挖掘,我发现了一个适用于GWT的Gradle插件: de.esoco.gwt 。 它是Putnami插件的分支。 该文档足以使该插件正常工作。 我没有发现任何重大问题。 build.gradle中的配置位于gwt块内:
gwt {
gwtVersion = gwtVersion
module("org.example.gwt.StockWatcher2", "de.richsource.gradle.plugins.gwt.example.Example")
// other configuration options
}
该插件将一些任务添加到了gradle构建中。 其中最重要的是gwtCompile 。 该任务实际上生成了javascript代码,并将其放入${buildDir}/gwt/out
。 这些值(gwt和out)都在Gradle GWT插件中进行了硬编码。
重要的是要记住,编译成javascript的代码是在GWT模块文件中指定的,如下所示:
<source path='client'/>
<source path='shared'/>
休息和休息
下一个目标是使用Spring Boot的REST端点。 我发现RestyGWT可以帮助我做到这一点。 他们在首页上有一个简单的方法。
我将所需的依赖项添加到build.gradle中:
implementation("javax.ws.rs:javax.ws.rs-api:2.0.1")
compileOnly group: "org.fusesource.restygwt", name: "restygwt", version: "2.2.0"
implementation group: "com.fasterxml.jackson.jaxrs", name: "jackson-jaxrs-json-provider", version: "2.8.9"
JAX-RS依赖关系是必需的,因为RestyGWT使用JAX-RS的注释来声明端点。 据我了解,Jackson也有必要解析JSON。
我也在GWT模块中添加了依赖项:
<inherits name="org.fusesource.restygwt.RestyGWT"/>
这是我用RestyGWT创建的服务:
public interface TestService extends RestService {
@GET
@Path("test") void test1(@QueryParam("input") String inp,
MethodCallback<TestResult> callback);
}
我在ClickHandler中调用此服务(我主要使用了原始GWT教程中的代码):
private final TestService testSrv = GWT.create(TestService.class);
btnCallServer.addClickHandler(clkEvent -> {
testSrv.test1("TestString", new MethodCallback<TestResult>() {
@Override
public void onSuccess(Method method, TestResult response) {
testLabel.setText("Srv success " + response.getStr1());
}
@Override
public void onFailure(Method method, Throwable exception) {
testLabel.setText("Srv failure " + exception.getMessage());
}
});
});
该服务在Spring Boot控制器中调用此简单方法:
@GetMapping("/test")
public TestResult test1(@RequestParam(name="input", required=false) String inp) {
return new TestResult(inp + " qqq");
}
好消息是所有这些代码都是一个可执行jar的一部分。
可执行罐
第三个目标是将所有内容实际捆绑到一个可执行文件胖子中。 在本部分中,我最终可以利用Gradle的灵活性。
首先,我将html文件放在/src/main/resources/static
。
我创建了一个任务,用于在生成过程中将生成的javascript复制到$ {buildDir}中的静态文件夹中:
task copyGWTCode(dependsOn: ["gwtCompile"], type: Copy) {
from file("${buildDir}/gwt/out")
into file("${buildDir}/resources/main/static")
}
接下来,我使bootJar
任务依赖于此任务,并将jar复制到更传统的目标目录中:
bootJar {
dependsOn copyGWTCode
doLast {
mkdir "${buildDir}/target"
setDestinationDir(file("${buildDir}/target"))
copy()
}
}
在GWT中进行调试
关于GWT调试的另一章。
我找到了一种在Chrome中调试GWT UI的相当简单的方法(Chrome可以比Firefox更好地处理它)。 以下是使其工作的步骤。 我使用了GWT教程中的项目,但将其重命名为“ stockwatcher2”。
1.在src/main/resources/static
添加一个新的html文件进行调试。 例如,如果原始文件为StockWatcher2.html,则新文件应为StockWatcher2debug.html。 在这个新文件中,替换该行
<script type="text/javascript" src="stockwatcher2/stockwatcher2.nocache.js"></script>
这行代码(来自代码服务器的javascript):
<script src="http://localhost:9876/recompile-requester/stockwatcher2"></script>
2.执行任务bootJar并运行它。
3.使用“ gradle gwtCodeServer”从项目文件夹启动代码服务器。
4.在Chrome中打开http://<host>:<port>/<somepath>/StockWatcher2debug.html
5.现在,您可以在Developer Tools-> Sources下的127.0.0.1:9876下找到源映射。 可以设置断点并直接在Chrome中点击。
使用单独文件的想法是将其从生产版本中排除,但将其保留在开发人员版本中。 使用Gradle很容易。 这种方法只有一个问题,那就是从调试源调用的REST端点与从“正常”源调用的REST端点不同。 再添加一个映射即可解决该问题。
结论
我祝贺已经得出这一结论的英雄人物! 你是真正的程序员,而那些放弃的人却是胆小鬼!
但最重要的是,与GWT合作非常困难。 生成工具非常笨拙,缺少重要功能。 实际上没有集成(例如与Spring Boot集成)。 调试是不必要的复杂操作。
如果有人要在GWT和Eclipse RAP之间进行选择,我会推荐Eclipse RAP。
没有幸福的结局:-(。
翻译自: https://www.javacodegeeks.com/2018/11/gwt-spring-boot.html