通过 Gradle 运行 Java 类时传递系统属性和参数的问题

Posted

技术标签:

【中文标题】通过 Gradle 运行 Java 类时传递系统属性和参数的问题【英文标题】:Problems passing system properties and parameters when running Java class via Gradle 【发布时间】:2014-07-04 12:56:23 【问题描述】:

我正在尝试通过 Gradle 运行命令行 Java 应用程序,作为快速集成测试的一部分。我正在从 Maven 移植我的构建脚本,这可以通过 exec-maven-plugin 轻松完成。我的两大要求是:

能够将系统属性传递给可执行的 Java 代码 能够将命令行参数传递给可执行的 Java 代码

请注意,我不是要在构建脚本中读取这些属性,而是要在脚本构建和执行的 Java 程序中读取它们。

我发现了另外两篇关于通过 Gradle 执行 Java 程序的 SO 帖子:one with an answer that advocates using apply plugin: "application" in the build file and gradle run at the command line 和 another with answers advocating that approach as well as using task execute(type:JavaExec) in the build file and gradle execute at the command line。这两种方法我都试过了,都没有成功。

我有两个问题:

(1) 我无法让 Java 可执行文件读取系统属性

我是否这样做:

build.gradle

apply plugin: 'application'
mainClassName = "com.mycompany.MyMain"

命令行

gradle run -Dmyproperty=myvalue

或者这个:

build.gradle

task execute (type:JavaExec) 
    main = "com.mycompany.MyMain"
    classpath = sourceSets.main.runtimeClasspath 

命令行

gradle execute -Dmyproperty=myvalue

在任何一种情况下,myproperty 都无法通过。从MyMain.main (...) 开始运行的代码将myproperty 系统属性读取为空/缺失。

(2) 我无法传递命令行参数

这可能与第一个问题有关。例如,在exec-maven-plugin 中,命令行参数本身是通过系统属性传入的。 Gradle 就是这种情况,还是有其他方法可以传递命令行参数?

如何通过这些变量?还有,是apply plugin: 'application'还是task execute (type:JavaExec)更好?

【问题讨论】:

systemProperties = System.properties as Map 【参考方案1】:

想通了。主要问题是当 Gradle 派生一个新的 Java 进程时,它不会自动将环境变量值传递给新环境。必须通过任务或插件的systemProperties 属性显式传递这些变量。

另一个问题是理解如何传递命令行参数;这些是通过任务或插件上的args 属性。与 Maven exec-maven-plugin 一样,它们应该通过另一个系统属性在命令行上传递,作为一个空格分隔的列表,然后需要在设置 args 之前为 split(),它接受 List 对象。我已将属性命名为 exec.args,这是 Maven 的旧名称。

似乎javaExec 和应用程序插件方法都有效。如果想使用它的一些其他功能(自动组合发行版等),可能会更喜欢应用程序插件方法

以下是解决方案:

JavaExec 方法

命令行

gradle execute -Dmyvariable=myvalue -Dexec.args="arg1 arg2 arg3"

build.gradle

task execute (type:JavaExec) 

    main = "com.myCompany.MyMain"
    classpath = sourceSets.main.runtimeClasspath 

    /* Can pass all the properties: */
    systemProperties System.getProperties()

    /* Or just each by name: */
    systemProperty "myvariable", System.getProperty("myvariable")

    /* Need to split the space-delimited value in the exec.args */
    args System.getProperty("exec.args", "").split()    

应用程序插件方法

命令行

gradle run -Dmyvariable=myvalue -Dexec.args="arg1 arg2 arg3"

build.gradle

apply plugin: 'application'
mainClassName = "com.mycompany.MyMain"
run     
    /* Can pass all the properties: */
    systemProperties System.getProperties()

    /* Or just each by name: */
    systemProperty "myvariable", System.getProperty("myvariable")

    /* Need to split the space-delimited value in the exec.args */
    args System.getProperty("exec.args", "").split()    

【讨论】:

我认为应该是 System.getProperties()(大写 S)。 如果你使用的是 gradle 2.xx,你也可以使用下面的:-systemProperty "myvariable", "$myvariable" 谢谢!我试图运行 Cucumber 测试并希望将环境变量传递给 JVM。我只是将“systemProperties System.getProperties()”放在测试 而不是运行 请注意,默认拆分不允许带空格的参数,例如-Dexec.args="--greeting 'hello there'" - 不起作用,需要设置另一个分隔符。 使用:args System.getProperty("exec.args", "").split() 如果没有提供参数,则避免空指针异常。例如,即使是“gradle tasks”也会抛出建议的 build.gradle 异常【参考方案2】:

对于那些可能不想通过传递不相关的 Gradle 道具来污染应用程序系统属性的人,我建议为参数命名空间。

tasks.withType(JavaExec) 
    System.properties.each  k,v->
        if (k.startsWith("prefix.")) 
            systemProperty k - "prefix.", v
        
    

java ... -Dprefix.my.prop=true 将通过my.prop

【讨论】:

【参考方案3】:

我是 gradle 新手,所以我需要这个,而且 gradle 4.6 对我有用的东西对于命令行来说似乎更容易一些。您可以传递一个 args 数组,而不是解析 1 个 arg 字符串,我找到了一种方法,也可以用一行传递所有属性。结合如下:

apply plugin: 'java'
apply plugin: 'org.springframework.boot'    <- for my project

task runApp(type: JavaExec) 
  classpath = sourceSets.main.runtimeClasspath

  main = 'testit.TomcatApp'

  // arguments to pass to the application
  //  args 'myarg1 -rest'    <- came in as 1 string

  args = ["--myarg1 with spaces even", "--myarg2"]

  // and to pass in all -D system property args:
  systemProperties = System.properties


gradle run -Dwhatever=xxx -Dmyarg2=hey

// Java reading them:
public static void main(String[] args) 
    for ( int i = 0; i < args.length; i++ )
        
        logger.info( "** args [" + i + "] =" + args[i] + "=" );
        
    logger.info( "** -Dwhatever =" + System.getProperty("whatever") + "=" );
    logger.info( "** -Dmyarg2 =" + System.getProperty("myarg2") + "=" );

[main] INFO testit.TomcatApp - ** args [0] =--myarg1 with spaces even=
[main] INFO testit.TomcatApp - ** args [1] =--myarg2=
[main] INFO testit.TomcatApp - ** -Dwhatever =xxx=
[main] INFO testit.TomcatApp - ** -Dmyarg2 =hey=

【讨论】:

【参考方案4】:

也许我参加聚会迟到了,但有没有人尝试过“在执行 gradle 之前设置道具”?我已经测试过了,这显然也有效。

myVar=myVal gradle test

例如,您可以将活动配置文件设置为:

SPRING_PROFILES_ACTIVE=dev  gradle test

显然,这些也有效:(已测试)

set myVar=myVal && gradle test      # for windows
export myVar=myVal && gradle test   # for linux and mac

注意myVar 不能用句点分隔;否则只有第一个时期之前的部分将被视为关键。

【讨论】:

以上是关于通过 Gradle 运行 Java 类时传递系统属性和参数的问题的主要内容,如果未能解决你的问题,请参考以下文章

通过 Eclipse 错误“系统找不到系统 Java 编译器”运行 Gradle 项目

将系统属性与 proguard 配置文件一起传递给 gradle proguardFile 属性

Eclipse在运行或调试期间没有使用gradle依赖jar

从 Gradle 引导传递动态端口:运行应用程序

从 Gradle 将系统属性传递到 Spring Boot

Java命令行运行参数