Skip to content

Commit ccca943

Browse files
committed
Add mainClassName to springBoot DSL in Gradle plugin
This commit introduces a new mainClassName property on the springBoot DSL provided by the Gradle plugin. It can be used to explicitly configure the mainClassName of the default bootRun, bootJar, and bootWar tasks in a single place. Previously, the only mechanism provided to do so was the mainClassName property that's only available when the application plugin has been applied. Closes gh-10623
1 parent 5502aaa commit ccca943

File tree

14 files changed

+242
-4
lines changed

14 files changed

+242
-4
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,16 @@ property:
9393
include::../gradle/packaging/boot-jar-main-class.gradle[tags=main-class]
9494
----
9595

96-
Alternatively, if the {application-plugin}[`application` plugin] has been applied
97-
its `mainClassName` project property can be used:
96+
Alternatively, the main class name can be configured project-wide using the
97+
`mainClassName` property of the Spring Boot DSL:
98+
99+
[source,groovy,indent=0,subs="verbatim"]
100+
----
101+
include::../gradle/packaging/spring-boot-dsl-main-class.gradle[tags=main-class]
102+
----
103+
104+
If the {application-plugin}[`application` plugin] has been applied its `mainClassName`
105+
project property can be used for the same purpose:
98106

99107
[source,groovy,indent=0,subs="verbatim"]
100108
----

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ classpath. The main class can also be configured explicitly using the task's
1919
include::../gradle/running/boot-run-main.gradle[tags=main]
2020
----
2121

22-
Alternatively, if the {application-plugin}[`application` plugin] has been applied
23-
its `mainClassName` project property can be used:
22+
Alternatively, the main class name can be configured project-wide using the
23+
`mainClassName` property of the Spring Boot DSL:
24+
25+
[source,groovy,indent=0,subs="verbatim"]
26+
----
27+
include::../gradle/running/spring-boot-dsl-main-class-name.gradle[tags=main-class]
28+
----
29+
30+
If the {application-plugin}[`application` plugin] has been applied its `mainClassName`
31+
project property can be used for the same purpose:
2432

2533
[source,groovy,indent=0,subs="verbatim"]
2634
----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
buildscript {
2+
dependencies {
3+
classpath files(pluginClasspath.split(','))
4+
}
5+
}
6+
7+
apply plugin: 'org.springframework.boot'
8+
apply plugin: 'java'
9+
10+
// tag::main-class[]
11+
springBoot {
12+
mainClassName = 'com.example.ExampleApplication'
13+
}
14+
// end::main-class[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
buildscript {
2+
dependencies {
3+
classpath files(pluginClasspath.split(','))
4+
}
5+
}
6+
7+
apply plugin: 'org.springframework.boot'
8+
apply plugin: 'java'
9+
apply plugin: 'application'
10+
11+
// tag::main-class[]
12+
springBoot {
13+
mainClassName = 'com.example.ExampleApplication'
14+
}
15+
// end::main-class[]
16+
17+
task configuredMainClass {
18+
doLast {
19+
println bootRun.mainClassName
20+
}
21+
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public class SpringBootExtension {
3939

4040
private final Project project;
4141

42+
private String mainClassName;
43+
4244
/**
4345
* Creates a new {@code SpringBootPluginExtension} that is associated with the given
4446
* {@code project}.
@@ -49,6 +51,24 @@ public SpringBootExtension(Project project) {
4951
this.project = project;
5052
}
5153

54+
/**
55+
* Returns the main class name of the application.
56+
*
57+
* @return the name of the application's main class
58+
*/
59+
public String getMainClassName() {
60+
return this.mainClassName;
61+
}
62+
63+
/**
64+
* Sets the main class name of the application.
65+
*
66+
* @param mainClassName the name of the application's main class
67+
*/
68+
public void setMainClassName(String mainClassName) {
69+
this.mainClassName = mainClassName;
70+
}
71+
5272
/**
5373
* Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the
5474
* Java plugin's {@code classes} task to depend upon it.

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MainClassConvention.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.gradle.api.Project;
2626
import org.gradle.api.file.FileCollection;
2727

28+
import org.springframework.boot.gradle.dsl.SpringBootExtension;
2829
import org.springframework.boot.loader.tools.MainClassFinder;
2930

3031
/**
@@ -47,6 +48,12 @@ final class MainClassConvention implements Callable<String> {
4748

4849
@Override
4950
public String call() throws Exception {
51+
SpringBootExtension springBootExtension = this.project.getExtensions()
52+
.findByType(SpringBootExtension.class);
53+
if (springBootExtension != null
54+
&& springBootExtension.getMainClassName() != null) {
55+
return springBootExtension.getMainClassName();
56+
}
5057
if (this.project.hasProperty("mainClassName")) {
5158
Object mainClassName = this.project.property("mainClassName");
5259
if (mainClassName != null) {

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@ public void applicationPluginMainClass() throws IOException {
8989
}
9090
}
9191

92+
@Test
93+
public void springBootDslMainClass() throws IOException {
94+
this.gradleBuild
95+
.script("src/main/gradle/packaging/spring-boot-dsl-main-class.gradle")
96+
.build("bootJar");
97+
File file = new File(this.gradleBuild.getProjectDir(),
98+
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
99+
assertThat(file).isFile();
100+
try (JarFile jar = new JarFile(file)) {
101+
assertThat(jar.getManifest().getMainAttributes().getValue("Start-Class"))
102+
.isEqualTo("com.example.ExampleApplication");
103+
}
104+
}
105+
92106
@Test
93107
public void bootWarIncludeDevtools() throws IOException {
94108
new File(this.gradleBuild.getProjectDir(),

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ public void applicationPluginMainClassName() throws IOException {
5151
.contains("com.example.ExampleApplication");
5252
}
5353

54+
@Test
55+
public void springBootDslMainClassName() throws IOException {
56+
assertThat(this.gradleBuild
57+
.script("src/main/gradle/running/spring-boot-dsl-main-class-name.gradle")
58+
.build("configuredMainClass").getOutput())
59+
.contains("com.example.ExampleApplication");
60+
}
61+
5462
@Test
5563
public void bootRunSourceResources() throws IOException {
5664
assertThat(this.gradleBuild
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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 org.springframework.boot.gradle.plugin;
18+
19+
import java.io.IOException;
20+
21+
import org.gradle.api.Project;
22+
import org.gradle.api.plugins.ExtraPropertiesExtension;
23+
import org.gradle.internal.impldep.org.junit.Before;
24+
import org.gradle.internal.impldep.org.junit.Rule;
25+
import org.gradle.internal.impldep.org.junit.Test;
26+
import org.gradle.internal.impldep.org.junit.rules.TemporaryFolder;
27+
import org.gradle.testfixtures.ProjectBuilder;
28+
29+
import org.springframework.boot.gradle.dsl.SpringBootExtension;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
33+
public class MainClassConventionTests {
34+
35+
@Rule
36+
public final TemporaryFolder temp = new TemporaryFolder();
37+
38+
private Project project;
39+
40+
private MainClassConvention convention;
41+
42+
@Before
43+
public void createConvention() throws IOException {
44+
this.project = ProjectBuilder.builder().withProjectDir(this.temp.newFolder())
45+
.build();
46+
this.convention = new MainClassConvention(this.project, () -> null);
47+
}
48+
49+
@Test
50+
public void mainClassNameProjectPropertyIsUsed() throws Exception {
51+
this.project.getExtensions().getByType(ExtraPropertiesExtension.class)
52+
.set("mainClassName", "com.example.MainClass");
53+
assertThat(this.convention.call()).isEqualTo("com.example.MainClass");
54+
}
55+
56+
@Test
57+
public void springBootExtensionMainClassNameIsUsed() throws Exception {
58+
SpringBootExtension extension = this.project.getExtensions().create("springBoot",
59+
SpringBootExtension.class, this.project);
60+
extension.setMainClassName("com.example.MainClass");
61+
assertThat(this.convention.call()).isEqualTo("com.example.MainClass");
62+
}
63+
64+
@Test
65+
public void springBootExtensionMainClassNameIsUsedInPreferenceToMainClassNameProjectProperty()
66+
throws Exception {
67+
this.project.getExtensions().getByType(ExtraPropertiesExtension.class)
68+
.set("mainClassName", "com.example.ProjectPropertyMainClass");
69+
SpringBootExtension extension = this.project.getExtensions().create("springBoot",
70+
SpringBootExtension.class, this.project);
71+
extension.setMainClassName("com.example.SpringBootExtensionMainClass");
72+
assertThat(this.convention.call())
73+
.isEqualTo("com.example.SpringBootExtensionMainClass");
74+
}
75+
76+
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,18 @@ public void applicationPluginMainClassNameIsUsed() throws IOException {
112112
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class"))
113113
.isEqualTo("com.example.CustomMain");
114114
}
115+
}
115116

117+
@Test
118+
public void springBootExtensionMainClassNameIsUsed() throws IOException {
119+
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName)
120+
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
121+
try (JarFile jarFile = new JarFile(
122+
new File(this.gradleBuild.getProjectDir(), "build/libs")
123+
.listFiles()[0])) {
124+
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class"))
125+
.isEqualTo("com.example.CustomMain");
126+
}
116127
}
117128

118129
@Test

0 commit comments

Comments
 (0)