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

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

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

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

我还发现了另外两篇通过 Gradle 处理 Java 程序执行的 SO 文章: 答案是主张在构建文件中使用 ABC0,在命令行中使用 gradle run另一个则支持这种方法,并在构建文件中使用 ABC2,在命令行中使用 gradle execute。这两种方法我都试过,但都没有成功。

我有两个问题:

(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系统属性读取为 null/miss。

(2)我不能传递命令行参数

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

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

75235 次浏览

Figured it out. The main issue is that when Gradle forks a new Java process, it does not automatically pass the environment variable values along to the new environment. One has to explicitly pass these variables via the systemProperties property of the task or plugin.

The other issue was understanding how to pass command-line args; these are via the args property on the task or plugin. As with the Maven exec-maven-plugin, they should be passed in on the command line via yet another system property, as a space-delimited list that then needs to be split() before setting args, which accepts List objects. I've named the property exec.args, which is the old Maven name.

It seems both the javaExec and application plugin approach are valid. One might favor the application plugin approach if one wants to use some of its other features (automatically putting together a distribution, etc.)

Here are the solutions:

JavaExec Approach

Command Line:

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()
}

Application Plugin Approach

Command Line:

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()
}

For those who might not want to pollute your application's system properties by passing unrelated Gradle props, I recommend namespacing your arguments.

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

java ... -Dprefix.my.prop=true will pass my.prop

I'm new to gradle so I needed this and what is working for me with gradle 4.6 seems a little easier for the command line. Instead of parsing 1 arg string you can pass an array of args, and I found a way to pass in all property with one line as well. Combined below:

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=

Maybe I am late for the party, but has anyone tried with "set the prop before executing gradle"? I have tested and this works too, apparently.

myVar=myVal gradle test

For example, you can set the active profile like:

SPRING_PROFILES_ACTIVE=dev  gradle test

These also work, apparently:(tested)

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

Be wary that myVar cannot be period-separated; or else only the part before the first period will be taken as key.