Default to optimized launch of the JVM when using Gradle's bootRun

Closes gh-16222
pull/16972/head
Andy Wilkinson 6 years ago
parent 7713a7f33a
commit 54b123028c

@ -8,11 +8,10 @@ To run your application without first building an archive use the `bootRun` task
$ ./gradlew bootRun $ ./gradlew bootRun
---- ----
The `bootRun` task is an instance of The `bootRun` task is an instance of {boot-run-javadoc}[`BootRun`] which is a `JavaExec`
{boot-run-javadoc}[`BootRun`] which is a `JavaExec` subclass. As such, all of the subclass. As such, all of the {gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual
{gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing configuration options] for executing a Java process in Gradle are available to you. The
a Java process in Gradle are available to you. The task is automatically configured to use task is automatically configured to use the runtime classpath of the main source set.
the runtime classpath of the main source set.
By default, the main class will be configured automatically by looking for a class with a By default, the main class will be configured automatically by looking for a class with a
`public static void main(String[])` method in directories on the task's classpath. `public static void main(String[])` method in directories on the task's classpath.
@ -48,6 +47,23 @@ include::../gradle/running/spring-boot-dsl-main-class-name.gradle.kts[tags=main-
---- ----
By default, `bootRun` will configure the JVM to optimize its launch for faster startup
during development. This behavior can be disabled by using the `optimizedLaunch` property,
as shown in the following example:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.Groovy
----
include::../gradle/running/boot-run-disable-optimized-launch.gradle[tags=launch]
----
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
.Kotlin
----
include::../gradle/running/boot-run-disable-optimized-launch.gradle.kts[tags=launch]
----
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` If the {application-plugin}[`application` plugin] has been applied, its `mainClassName`
project property must be configured and can be used for the same purpose: project property must be configured and can be used for the same purpose:

@ -0,0 +1,16 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
// tag::launch[]
bootRun {
optimizedLaunch = false
}
// end::launch[]
task optimizedLaunch {
doLast {
println bootRun.optimizedLaunch
}
}

@ -0,0 +1,18 @@
import org.springframework.boot.gradle.tasks.run.BootRun
plugins {
java
id("org.springframework.boot") version "{version}"
}
// tag::launch[]
tasks.getByName<BootRun>("bootRun") {
isOptimizedLaunch = false
}
// end::launch[]
task("optimizedLaunch") {
doLast {
println(tasks.getByName<BootRun>("bootRun").isOptimizedLaunch)
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.boot.gradle.tasks.run; package org.springframework.boot.gradle.tasks.run;
import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetOutput; import org.gradle.api.tasks.SourceSetOutput;
@ -29,6 +30,29 @@ import org.gradle.api.tasks.SourceSetOutput;
*/ */
public class BootRun extends JavaExec { public class BootRun extends JavaExec {
private boolean optimizedLaunch = true;
/**
* Returns {@code true} if the JVM's launch should be optimized, otherwise
* {@code false}. Defaults to {@code true}.
* @return whether the JVM's launch should be optimized
* @since 2.2.0
*/
@Input
public boolean isOptimizedLaunch() {
return this.optimizedLaunch;
}
/**
* Sets whether the JVM's launch should be optimized. Defaults to {@code true}.
* @param optimizedLaunch {@code true} if the JVM's launch should be optimised,
* otherwise {@code false}
* @since 2.2.0
*/
public void setOptimizedLaunch(boolean optimizedLaunch) {
this.optimizedLaunch = optimizedLaunch;
}
/** /**
* Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given * Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given
* {@code sourceSet's} {@link SourceSet#getResources() resources} to the start of the * {@code sourceSet's} {@link SourceSet#getResources() resources} to the start of the
@ -44,6 +68,10 @@ public class BootRun extends JavaExec {
@Override @Override
public void exec() { public void exec() {
if (this.optimizedLaunch) {
setJvmArgs(getJvmArgs());
jvmArgs("-Xverify:none", "-XX:TieredStopAtLevel=1");
}
if (System.console() != null) { if (System.console() != null) {
// Record that the console is available here for AnsiOutput to detect later // Record that the console is available here for AnsiOutput to detect later
this.getEnvironment().put("spring.output.ansi.console-available", true); this.getEnvironment().put("spring.output.ansi.console-available", true);

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,19 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package com.example; package com.example.classpath;
import java.io.File; import java.io.File;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
/** /**
* Very basic application used for testing {@code BootRun}. * Application used for testing {@code BootRun}'s classpath handling.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class BootRunApplication { public class BootRunClasspathApplication {
protected BootRunApplication() { protected BootRunClasspathApplication() {
} }

@ -0,0 +1,39 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.jvmargs;
import java.lang.management.ManagementFactory;
/**
* Application used for testing {@code BootRun}'s JVM argument handling.
*
* @author Andy Wilkinson
*/
public class BootRunJvmArgsApplication {
protected BootRunJvmArgsApplication() {
}
public static void main(String[] args) {
int i = 1;
for (String entry : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
System.out.println(i++ + ". " + entry);
}
}
}

@ -69,4 +69,11 @@ public class RunningDocumentationTests {
.contains(new File("src/main/resources").getPath()); .contains(new File("src/main/resources").getPath());
} }
@TestTemplate
public void bootRunDisableOptimizedLaunch() throws IOException {
assertThat(this.gradleBuild
.script("src/main/gradle/running/boot-run-disable-optimized-launch")
.build("optimizedLaunch").getOutput()).contains("false");
}
} }

@ -42,7 +42,7 @@ public class BootRunIntegrationTests {
@TestTemplate @TestTemplate
public void basicExecution() throws IOException { public void basicExecution() throws IOException {
copyApplication(); copyClasspathApplication();
new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs(); new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs();
BuildResult result = this.gradleBuild.build("bootRun"); BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
@ -56,7 +56,7 @@ public class BootRunIntegrationTests {
@TestTemplate @TestTemplate
public void sourceResourcesCanBeUsed() throws IOException { public void sourceResourcesCanBeUsed() throws IOException {
copyApplication(); copyClasspathApplication();
BuildResult result = this.gradleBuild.build("bootRun"); BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()) assertThat(result.getOutput())
@ -87,28 +87,56 @@ public class BootRunIntegrationTests {
@TestTemplate @TestTemplate
public void applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException { public void applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException {
copyApplication(); copyClasspathApplication();
BuildResult result = this.gradleBuild.build("echoMainClassName"); BuildResult result = this.gradleBuild.build("echoMainClassName");
assertThat(result.task(":echoMainClassName").getOutcome()) assertThat(result.task(":echoMainClassName").getOutcome())
.isEqualTo(TaskOutcome.SUCCESS); .isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()) assertThat(result.getOutput()).contains(
.contains("Main class name = com.example.BootRunApplication"); "Main class name = com.example.classpath.BootRunClasspathApplication");
}
@TestTemplate
public void defaultJvmArgs() throws IOException {
copyJvmArgsApplication();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("1. -Xverify:none")
.contains("2. -XX:TieredStopAtLevel=1");
}
@TestTemplate
public void optimizedLaunchDisabledJvmArgs() throws IOException {
copyJvmArgsApplication();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).doesNotContain("-Xverify:none")
.doesNotContain("-XX:TieredStopAtLevel=1");
} }
@TestTemplate @TestTemplate
public void applicationPluginJvmArgumentsAreUsed() throws IOException { public void applicationPluginJvmArgumentsAreUsed() throws IOException {
BuildResult result = this.gradleBuild.build("echoJvmArguments"); copyJvmArgsApplication();
assertThat(result.task(":echoJvmArguments").getOutcome()) BuildResult result = this.gradleBuild.build("bootRun");
.isEqualTo(TaskOutcome.UP_TO_DATE); assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()) assertThat(result.getOutput()).contains("1. -Dcom.bar=baz")
.contains("JVM arguments = [-Dcom.foo=bar, -Dcom.bar=baz]"); .contains("2. -Dcom.foo=bar").contains("3. -Xverify:none")
.contains("4. -XX:TieredStopAtLevel=1");
}
private void copyClasspathApplication() throws IOException {
copyApplication("classpath");
}
private void copyJvmArgsApplication() throws IOException {
copyApplication("jvmargs");
} }
private void copyApplication() throws IOException { private void copyApplication(String name) throws IOException {
File output = new File(this.gradleBuild.getProjectDir(), File output = new File(this.gradleBuild.getProjectDir(),
"src/main/java/com/example"); "src/main/java/com/example/" + name);
output.mkdirs(); output.mkdirs();
FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), output); FileSystemUtils.copyRecursively(new File("src/test/java/com/example/" + name),
output);
} }
private String canonicalPathOf(String path) throws IOException { private String canonicalPathOf(String path) throws IOException {

@ -4,7 +4,3 @@ plugins {
} }
applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz'] applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz']
task echoJvmArguments {
println 'JVM arguments = ' + bootRun.jvmArgs
}

Loading…
Cancel
Save