在 Java 应用程序中为开发和发布提供各种 URL 端点的正确方法是啥?

Posted

技术标签:

【中文标题】在 Java 应用程序中为开发和发布提供各种 URL 端点的正确方法是啥?【英文标题】:What's the proper way to have various URL endpoints for dev and release in a Java application?在 Java 应用程序中为开发和发布提供各种 URL 端点的正确方法是什么? 【发布时间】:2018-02-18 03:34:03 【问题描述】:

我正在使用 JavaFX、Gradle、javafx-gradle-plugin 构建 Java 桌面应用程序。此应用程序连接到我也构建的服务器。当我编译发布版本时,运行gradle jfxNative,我希望它与生产服务器通信;但除此之外,我希望它与 localhost 对话。

处理此问题的正确 Java/Gradle 方式是什么?某种编译配置文件?

【问题讨论】:

请查看answer 你有例子吗?您如何开始开发和生产?有不同的任务? @Opal:这是一个桌面应用程序,生产人员安装它然后双击图标(或其他)。对于开发,我通常从 IntelliJ 或 gradle run 运行它。 谢谢!那么你如何区分这是否是生产版本?您现在如何处理服务器 URL? @Opal:好吧,我不知道,这就是问题所在。怎么做。现在,当我正在开发时,该应用程序被硬编码以连接到 localhost。理想情况下,当我构建应用程序以进行分发(gradle jfxNative)时,我希望它默认为生产服务器。 【参考方案1】:

您可以为此使用 Gradle 的 source sets:

示例 build.gradle:

apply plugin: 'java'

sourceSets 
    prod 
        java 
            srcDirs = ['src/main/java', 'src/prod/java']
        
    
    dev 
        java 
            srcDirs = ['src/main/java', 'src/dev/java']
        
    


task devJar(type: Jar) 
    from sourceSets.dev.output
    manifest 
        attributes("Main-Class": "MyPackage.MyClass")
    


task prodJar(type: Jar) 
    from sourceSets.prod.output
    manifest 
        attributes("Main-Class": "MyPackage.MyClass")
    

现在您可以为您的 dev 和 prod 版本创建两个配置类: src/dev/java/MyPackage/Configuration.javasrc/prod/java/MyPackage/Configuration.java

所有的通用代码都在主源码集中:src/main/java/MyPackage/MyClass.java

MyClass 可以从配置类中获取一些值(例如Configuration.getBaseUrl()

运行 gradle devJar/ gradle prodJar 构建变体之一。

注意:在您的情况下,您可能需要扩展 jfxNative/jfxJar 而不是 Jar

【讨论】:

我认为这是一个配置问题:“我希望 [prod] 与生产服务器对话;否则,我希望它与 localhost 对话”,测试之间的代码不需要不同和 prod (事实上,如果没有差异会更好,因为这样你的测试会更真实) 如果您希望将配置存储在文本文件中,@Rich Source 集同样适用于资源文件(请参阅我的答案顶部的链接)。 在构建时在 JAR 中包含不同的资源文件是解决问题的绝佳方法,我同意。我的评论指的是您写的答案,目前似乎建议放入不同的 Java 代码:“src/prod/java”。【参考方案2】:

最简单的解决方案:拥有一个包含此类信息的配置文件。 您可以将其作为 java 资源编译到应用程序中,也可以将其放在 jar 文件旁边,以便通过文件系统轻松查找。

使用 gradle,您只需定义两个具有不同输入属性的构建任务,然后使用 groovy 模板将值插入到属性文件中。

src/main/resources 中的application.properties:

server.address=$serverAddress

添加到您的 build.gradle

task setProductionServerAddress 
    processResources.expand([serverAddress: "https://app.example.com/v1"])

jfxJar.dependsOn(setProductionServerAddress)
jfxNative.dependsOn(setProductionServerAddress)

然后在应用程序上:

Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/application.properties"));
if (properties.getProperty("server.address").equals("$serverAddress")) 
  setUrl("http://localhost:8080/v1");
 else 
  setUrl(properties.getProperty("server.address"));

【讨论】:

这是有希望的,但最终 JavaFX 程序的构建发生在 jfxNative 中,这是已经提供的任务。我尝试将 processResources 添加到 jfxNative 所依赖的任务中,但它不起作用(尚不知道为什么)。【参考方案3】:

让它检查环境变量以获取配置文件的名称。与 gradle 或 build 无关。无论部署在哪里,同一个程序都应该可以正常运行。

参见,例如,Properties for dev and production

最简单的方法是定义一个系统属性,该属性指定数据的文件系统位置。生产应用服务器将定义一个值(在启动脚本中使用 java -D),而您的开发应用服务器将定义另一个值。您的应用程序源将查询系统属性值(使用 System.getProperty())以发现适当的位置

另外,这是有道理的。

将您需要的信息放入 JNDI - 这就是它的设计目的。 如果信息不存在,请考虑让您的应用程序拒绝执行任何操作。

另一个参考:What is the best way to manage configuration data

编辑:嗯,在我看来,你所问的在逻辑上是不可能的。 “它应该连接到生产,除非某个特定的人想要连接到开发,但该功能应该只对未知的人可用”开始菜单只是运行应用程序的快捷方式,因此您可以安装“开发”快捷方式读取为环境变量的命令行设置。

【讨论】:

未部署。这是一个桌面应用程序,因此,环境变量确实有效,但很烦人。您必须记住在运行应用程序时手动设置它们,因此非常容易出错。 @Pablo 如果在没有提供任何参数的情况下将 localhost 设置为默认环境会怎样(以避免意外与生产环境通信)? 您可以在系统中设置或使用批处理文件先设置它们然后运行应用程序。 @Serhiy 部署什么来生产? @KarlNicholas 如果我在系统中设置它,那么问题就反过来了,我需要记住撤消它们以连接到 prod。我什至无法运行我的应用,因为它会连接到错误的服务器。【参考方案4】:

我会选择 “12 因素应用程序” 概念之一,可以阅读 here

它的一个主要概念是使用系统环境变量,它应该确定您是在使用 prod 还是 dev 或 qa env 等。 每个项目/环境/机器都应包含其相关的 env 属性,然后可以通过类似于 maven 配置文件插件的 gradle 过程检索。

如何检测的示例:

`if (project.hasProperty('env') && project.getProperty('env') == 'prod') 
    apply from: 'gradle/production.gradle'
 else 
    apply from: 'gradle/development.gradle'
`

更多关于使用 gradle 的方法可以找到:gradle profile

【讨论】:

这个项目对象是什么?我熟悉 12 因子,并将其用于我的服务器应用程序。 项目是 gradle 的对象之一。从命令行构建 gradle 时,您可以传递 -Penv=prod ,这将使 gradle 通过上面的 if 条件。更多关于 gradle 项目here【参考方案5】:

在我看来,就像其他人所建议的那样,这与构建无关,而与运行时有关。

因此,您可以求助于检查某种运行时标志 - 一种方便且经常使用的方法是使用系统属性。

在您的开发框上,您可以设置一个环境变量 - 可以说 FX _DESKTOP_APP_ENV = DEV 或类似的。

从您的代码中,您可以查找并决定要使用的 URL。

String env = System.getenv("FX _DESKTOP_APP_ENV");
String url = env == null ? "Production" : env;

在 Windows 系统上,您可以像这样设置系统环境变量 -- enter link description here

在 *nix 系统上enter link description here

希望对你有帮助

【讨论】:

【参考方案6】:

您需要选择一个配置方案(如果 JavaFX 没有为您选择一个)。

我喜欢https://github.com/typesafehub/config。

配置库将包含有关如何使“生产”配置与“开发”配置不同的说明。

另见JavaFX:Editable Configuration Files After Packaging

处理此问题的正确 Java/Gradle 方式是什么?某种编译配置文件?

不,我强烈建议不要为生产和生产环境编译不同的代码。测试。这将使您的测试无效。这应该在配置中处理,而不是在代码中。传统的 Java 方法是使用配置文件(可以作为资源编译到 JAR 中)。

如何使用 Typesafe Config 做到这一点

我看了一点,很惊讶没有找到高质量的教程,我可以在这里链接你,抱歉(我发现了一些垃圾)。也许这个问题会成为其他人的参考。

我会这样做:

按照https://***.com/a/33261928/8261 的行创建一个“dev”和“prod”配置文件 根据Can you tell on runtime if you're running java from within a jar?,安排您的应用在 IDE 中运行时使用“dev”配置文件,并在从已编译的 JAR 中运行时使用“prod”配置文件

我猜为什么没有很多教程,因为所有应用程序或 Web 框架都会为您处理这个问题。

【讨论】:

仍然,我如何使生产完成的二进制文件连接到一个 URL,而开发的二进制文件连接到另一个。 此解决方案再次恢复为使用系统属性/环境变量。 Gradle 足以在没有它们的情况下进行不同的 prod/dev/test 构建。 我同意你的观点,@dev.bmax,系统属性/环境变量在这里是不可取的。尽管我没有时间编写有效的 POC,但我已经更新了答案以显示我的想法。我同意按照您的建议在不同的资源文件中编译是一个很好的解决方案。

以上是关于在 Java 应用程序中为开发和发布提供各种 URL 端点的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JAVA 在 Windows 中为自定义硬件编写 WMI 提供程序

如何在 Django 1.4 中为本地开发提供静态文件

Java的各种加密算法

Java中的各种加密算法

在 Windows 中为 PhoneGap 构建 iOS 签名密钥

如何在 Java 应用程序中为我的菜单栏设置键盘快捷键?