Skip to content

Commit 54b1230

Browse files
committed
Default to optimized launch of the JVM when using Gradle's bootRun
Closes gh-16222
1 parent 7713a7f commit 54b1230

File tree

11 files changed

+188
-28
lines changed

11 files changed

+188
-28
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ To run your application without first building an archive use the `bootRun` task
88
$ ./gradlew bootRun
99
----
1010

11-
The `bootRun` task is an instance of
12-
{boot-run-javadoc}[`BootRun`] which is a `JavaExec` subclass. As such, all of the
13-
{gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing
14-
a Java process in Gradle are available to you. The task is automatically configured to use
15-
the runtime classpath of the main source set.
11+
The `bootRun` task is an instance of {boot-run-javadoc}[`BootRun`] which is a `JavaExec`
12+
subclass. As such, all of the {gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual
13+
configuration options] for executing a Java process in Gradle are available to you. The
14+
task is automatically configured to use the runtime classpath of the main source set.
1615

1716
By default, the main class will be configured automatically by looking for a class with a
1817
`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-
4847
----
4948

5049

50+
By default, `bootRun` will configure the JVM to optimize its launch for faster startup
51+
during development. This behavior can be disabled by using the `optimizedLaunch` property,
52+
as shown in the following example:
53+
54+
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
55+
.Groovy
56+
----
57+
include::../gradle/running/boot-run-disable-optimized-launch.gradle[tags=launch]
58+
----
59+
60+
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
61+
.Kotlin
62+
----
63+
include::../gradle/running/boot-run-disable-optimized-launch.gradle.kts[tags=launch]
64+
----
65+
66+
5167
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName`
5268
project property must be configured and can be used for the same purpose:
5369

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id 'java'
3+
id 'org.springframework.boot' version '{version}'
4+
}
5+
6+
// tag::launch[]
7+
bootRun {
8+
optimizedLaunch = false
9+
}
10+
// end::launch[]
11+
12+
task optimizedLaunch {
13+
doLast {
14+
println bootRun.optimizedLaunch
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import org.springframework.boot.gradle.tasks.run.BootRun
2+
3+
plugins {
4+
java
5+
id("org.springframework.boot") version "{version}"
6+
}
7+
8+
// tag::launch[]
9+
tasks.getByName<BootRun>("bootRun") {
10+
isOptimizedLaunch = false
11+
}
12+
// end::launch[]
13+
14+
task("optimizedLaunch") {
15+
doLast {
16+
println(tasks.getByName<BootRun>("bootRun").isOptimizedLaunch)
17+
}
18+
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.gradle.tasks.run;
1818

1919
import org.gradle.api.file.SourceDirectorySet;
20+
import org.gradle.api.tasks.Input;
2021
import org.gradle.api.tasks.JavaExec;
2122
import org.gradle.api.tasks.SourceSet;
2223
import org.gradle.api.tasks.SourceSetOutput;
@@ -29,6 +30,29 @@
2930
*/
3031
public class BootRun extends JavaExec {
3132

33+
private boolean optimizedLaunch = true;
34+
35+
/**
36+
* Returns {@code true} if the JVM's launch should be optimized, otherwise
37+
* {@code false}. Defaults to {@code true}.
38+
* @return whether the JVM's launch should be optimized
39+
* @since 2.2.0
40+
*/
41+
@Input
42+
public boolean isOptimizedLaunch() {
43+
return this.optimizedLaunch;
44+
}
45+
46+
/**
47+
* Sets whether the JVM's launch should be optimized. Defaults to {@code true}.
48+
* @param optimizedLaunch {@code true} if the JVM's launch should be optimised,
49+
* otherwise {@code false}
50+
* @since 2.2.0
51+
*/
52+
public void setOptimizedLaunch(boolean optimizedLaunch) {
53+
this.optimizedLaunch = optimizedLaunch;
54+
}
55+
3256
/**
3357
* Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given
3458
* {@code sourceSet's} {@link SourceSet#getResources() resources} to the start of the
@@ -44,6 +68,10 @@ public void sourceResources(SourceSet sourceSet) {
4468

4569
@Override
4670
public void exec() {
71+
if (this.optimizedLaunch) {
72+
setJvmArgs(getJvmArgs());
73+
jvmArgs("-Xverify:none", "-XX:TieredStopAtLevel=1");
74+
}
4775
if (System.console() != null) {
4876
// Record that the console is available here for AnsiOutput to detect later
4977
this.getEnvironment().put("spring.output.ansi.console-available", true);

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java renamed to spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/classpath/BootRunClasspathApplication.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,19 +14,19 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.example;
17+
package com.example.classpath;
1818

1919
import java.io.File;
2020
import java.lang.management.ManagementFactory;
2121

2222
/**
23-
* Very basic application used for testing {@code BootRun}.
23+
* Application used for testing {@code BootRun}'s classpath handling.
2424
*
2525
* @author Andy Wilkinson
2626
*/
27-
public class BootRunApplication {
27+
public class BootRunClasspathApplication {
2828

29-
protected BootRunApplication() {
29+
protected BootRunClasspathApplication() {
3030

3131
}
3232

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.jvmargs;
18+
19+
import java.lang.management.ManagementFactory;
20+
21+
/**
22+
* Application used for testing {@code BootRun}'s JVM argument handling.
23+
*
24+
* @author Andy Wilkinson
25+
*/
26+
public class BootRunJvmArgsApplication {
27+
28+
protected BootRunJvmArgsApplication() {
29+
30+
}
31+
32+
public static void main(String[] args) {
33+
int i = 1;
34+
for (String entry : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
35+
System.out.println(i++ + ". " + entry);
36+
}
37+
}
38+
39+
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,11 @@ public void bootRunSourceResources() throws IOException {
6969
.contains(new File("src/main/resources").getPath());
7070
}
7171

72+
@TestTemplate
73+
public void bootRunDisableOptimizedLaunch() throws IOException {
74+
assertThat(this.gradleBuild
75+
.script("src/main/gradle/running/boot-run-disable-optimized-launch")
76+
.build("optimizedLaunch").getOutput()).contains("false");
77+
}
78+
7279
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class BootRunIntegrationTests {
4242

4343
@TestTemplate
4444
public void basicExecution() throws IOException {
45-
copyApplication();
45+
copyClasspathApplication();
4646
new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs();
4747
BuildResult result = this.gradleBuild.build("bootRun");
4848
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
@@ -56,7 +56,7 @@ public void basicExecution() throws IOException {
5656

5757
@TestTemplate
5858
public void sourceResourcesCanBeUsed() throws IOException {
59-
copyApplication();
59+
copyClasspathApplication();
6060
BuildResult result = this.gradleBuild.build("bootRun");
6161
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
6262
assertThat(result.getOutput())
@@ -87,28 +87,56 @@ public void applicationPluginMainClassNameIsUsed() throws IOException {
8787

8888
@TestTemplate
8989
public void applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException {
90-
copyApplication();
90+
copyClasspathApplication();
9191
BuildResult result = this.gradleBuild.build("echoMainClassName");
9292
assertThat(result.task(":echoMainClassName").getOutcome())
9393
.isEqualTo(TaskOutcome.SUCCESS);
94-
assertThat(result.getOutput())
95-
.contains("Main class name = com.example.BootRunApplication");
94+
assertThat(result.getOutput()).contains(
95+
"Main class name = com.example.classpath.BootRunClasspathApplication");
96+
}
97+
98+
@TestTemplate
99+
public void defaultJvmArgs() throws IOException {
100+
copyJvmArgsApplication();
101+
BuildResult result = this.gradleBuild.build("bootRun");
102+
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
103+
assertThat(result.getOutput()).contains("1. -Xverify:none")
104+
.contains("2. -XX:TieredStopAtLevel=1");
105+
}
106+
107+
@TestTemplate
108+
public void optimizedLaunchDisabledJvmArgs() throws IOException {
109+
copyJvmArgsApplication();
110+
BuildResult result = this.gradleBuild.build("bootRun");
111+
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
112+
assertThat(result.getOutput()).doesNotContain("-Xverify:none")
113+
.doesNotContain("-XX:TieredStopAtLevel=1");
96114
}
97115

98116
@TestTemplate
99117
public void applicationPluginJvmArgumentsAreUsed() throws IOException {
100-
BuildResult result = this.gradleBuild.build("echoJvmArguments");
101-
assertThat(result.task(":echoJvmArguments").getOutcome())
102-
.isEqualTo(TaskOutcome.UP_TO_DATE);
103-
assertThat(result.getOutput())
104-
.contains("JVM arguments = [-Dcom.foo=bar, -Dcom.bar=baz]");
118+
copyJvmArgsApplication();
119+
BuildResult result = this.gradleBuild.build("bootRun");
120+
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
121+
assertThat(result.getOutput()).contains("1. -Dcom.bar=baz")
122+
.contains("2. -Dcom.foo=bar").contains("3. -Xverify:none")
123+
.contains("4. -XX:TieredStopAtLevel=1");
124+
}
125+
126+
private void copyClasspathApplication() throws IOException {
127+
copyApplication("classpath");
128+
}
129+
130+
private void copyJvmArgsApplication() throws IOException {
131+
copyApplication("jvmargs");
105132
}
106133

107-
private void copyApplication() throws IOException {
134+
private void copyApplication(String name) throws IOException {
108135
File output = new File(this.gradleBuild.getProjectDir(),
109-
"src/main/java/com/example");
136+
"src/main/java/com/example/" + name);
110137
output.mkdirs();
111-
FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), output);
138+
FileSystemUtils.copyRecursively(new File("src/test/java/com/example/" + name),
139+
output);
112140
}
113141

114142
private String canonicalPathOf(String path) throws IOException {

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,3 @@ plugins {
44
}
55

66
applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz']
7-
8-
task echoJvmArguments {
9-
println 'JVM arguments = ' + bootRun.jvmArgs
10-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
plugins {
2+
id 'application'
3+
id 'org.springframework.boot' version '{version}'
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
id 'application'
3+
id 'org.springframework.boot' version '{version}'
4+
}
5+
6+
bootRun {
7+
optimizedLaunch = false
8+
}

0 commit comments

Comments
 (0)