Merge branch '2.2.x'
Closes gh-18957
This commit is contained in:
@@ -41,7 +41,7 @@ Explicit build support is provided for the following build tools:
|
||||
| 3.3+
|
||||
|
||||
| Gradle
|
||||
| 5.x (4.10 is also supported but in a deprecated form)
|
||||
| 5.x and 6.x (4.10 is also supported but in a deprecated form)
|
||||
|===
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ In those cases, see <<using-spring-boot.adoc#using-boot-maven-without-a-parent>>
|
||||
|
||||
[[getting-started-gradle-installation]]
|
||||
==== Gradle Installation
|
||||
Spring Boot is compatible with 5.x.
|
||||
Spring Boot is compatible with 5.x and 6.x.
|
||||
4.10 is also supported but this support is deprecated and will be removed in a future release.
|
||||
If you do not already have Gradle installed, you can follow the instructions at https://gradle.org.
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ Andy Wilkinson
|
||||
|
||||
The Spring Boot Gradle Plugin provides Spring Boot support in https://gradle.org[Gradle].
|
||||
It allows you to package executable jar or war archives, run Spring Boot applications, and use the dependency management provided by `spring-boot-dependencies`.
|
||||
Spring Boot's Gradle plugin requires Gradle 5.x (4.10 is also supported but this support is deprecated and will be removed in a future release).
|
||||
Spring Boot's Gradle plugin requires Gradle 5.x or 6.x (4.10 is also supported but this support is deprecated and will be removed in a future release).
|
||||
|
||||
In addition to this user guide, {api-documentation}[API documentation] is also available.
|
||||
|
||||
|
||||
@@ -49,4 +49,4 @@ include::../gradle/publishing/maven-publish.gradle.kts[tags=publishing]
|
||||
When the {application-plugin}[`application` plugin] is applied a distribution named `boot` is created.
|
||||
This distribution contains the archive produced by the `bootJar` or `bootWar` task and scripts to launch it on Unix-like platforms and Windows.
|
||||
Zip and tar distributions can be built by the `bootDistZip` and `bootDistTar` tasks respectively.
|
||||
To use the `application` plugin, its `mainClassName` project property must be configured with the name of your application's main class.
|
||||
To use the `application` plugin, its `mainClassName` property must be configured with the name of your application's main class.
|
||||
|
||||
@@ -60,7 +60,7 @@ include::../gradle/running/boot-run-disable-optimized-launch.gradle.kts[tags=lau
|
||||
----
|
||||
|
||||
|
||||
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` project property must be configured and can be used for the same purpose:
|
||||
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` property must be configured and can be used for the same purpose:
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Groovy
|
||||
|
||||
@@ -5,7 +5,9 @@ plugins {
|
||||
}
|
||||
|
||||
// tag::main-class[]
|
||||
mainClassName = 'com.example.ExampleApplication'
|
||||
application {
|
||||
mainClassName = 'com.example.ExampleApplication'
|
||||
}
|
||||
// end::main-class[]
|
||||
|
||||
task configuredMainClass {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.gradle.dsl;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Project;
|
||||
@@ -24,6 +25,7 @@ import org.gradle.api.plugins.BasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
|
||||
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo;
|
||||
@@ -115,7 +117,7 @@ public class SpringBootExtension {
|
||||
|
||||
private String determineArtifactBaseName() {
|
||||
Jar artifactTask = findArtifactTask();
|
||||
return (artifactTask != null) ? artifactTask.getBaseName() : null;
|
||||
return (artifactTask != null) ? getArchiveBaseName(artifactTask) : null;
|
||||
}
|
||||
|
||||
private Jar findArtifactTask() {
|
||||
@@ -126,4 +128,26 @@ public class SpringBootExtension {
|
||||
return (Jar) this.project.getTasks().findByName("bootJar");
|
||||
}
|
||||
|
||||
private static String getArchiveBaseName(AbstractArchiveTask task) {
|
||||
try {
|
||||
Method method = findMethod(task.getClass(), "getArchiveBaseName");
|
||||
if (method != null) {
|
||||
return (String) method.invoke(task);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
return task.getBaseName();
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> type, String name) {
|
||||
for (Method candidate : type.getMethods()) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
@@ -32,6 +33,8 @@ import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.internal.IConventionAware;
|
||||
import org.gradle.api.plugins.ApplicationPlugin;
|
||||
import org.gradle.api.plugins.ApplicationPluginConvention;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator;
|
||||
|
||||
import org.springframework.boot.gradle.tasks.application.CreateBootStartScripts;
|
||||
@@ -49,10 +52,7 @@ final class ApplicationPluginAction implements PluginApplicationAction {
|
||||
.getPlugin(ApplicationPluginConvention.class);
|
||||
DistributionContainer distributions = project.getExtensions().getByType(DistributionContainer.class);
|
||||
Distribution distribution = distributions.create("boot");
|
||||
if (distribution instanceof IConventionAware) {
|
||||
((IConventionAware) distribution).getConventionMapping().map("baseName",
|
||||
() -> applicationConvention.getApplicationName() + "-boot");
|
||||
}
|
||||
configureBaseNameConvention(project, applicationConvention, distribution);
|
||||
CreateBootStartScripts bootStartScripts = project.getTasks().create("bootStartScripts",
|
||||
CreateBootStartScripts.class);
|
||||
bootStartScripts
|
||||
@@ -79,6 +79,37 @@ final class ApplicationPluginAction implements PluginApplicationAction {
|
||||
distribution.getContents().with(binCopySpec);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configureBaseNameConvention(Project project, ApplicationPluginConvention applicationConvention,
|
||||
Distribution distribution) {
|
||||
Method getDistributionBaseName = findMethod(distribution.getClass(), "getDistributionBaseName");
|
||||
if (getDistributionBaseName != null) {
|
||||
try {
|
||||
Property<String> distributionBaseName = (Property<String>) distribution.getClass()
|
||||
.getMethod("getDistributionBaseName").invoke(distribution);
|
||||
distributionBaseName.getClass().getMethod("convention", Provider.class).invoke(distributionBaseName,
|
||||
project.provider(() -> applicationConvention.getApplicationName() + "-boot"));
|
||||
return;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
if (distribution instanceof IConventionAware) {
|
||||
((IConventionAware) distribution).getConventionMapping().map("baseName",
|
||||
() -> applicationConvention.getApplicationName() + "-boot");
|
||||
}
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> type, String name) {
|
||||
for (Method candidate : type.getMethods()) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Plugin<Project>> getPluginClass() {
|
||||
return ApplicationPlugin.class;
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.function.Supplier;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.plugins.JavaApplication;
|
||||
|
||||
import org.springframework.boot.gradle.dsl.SpringBootExtension;
|
||||
import org.springframework.boot.loader.tools.MainClassFinder;
|
||||
@@ -53,11 +54,9 @@ final class MainClassConvention implements Callable<Object> {
|
||||
if (springBootExtension != null && springBootExtension.getMainClassName() != null) {
|
||||
return springBootExtension.getMainClassName();
|
||||
}
|
||||
if (this.project.hasProperty("mainClassName")) {
|
||||
Object mainClassName = this.project.property("mainClassName");
|
||||
if (mainClassName != null) {
|
||||
return mainClassName;
|
||||
}
|
||||
JavaApplication javaApplication = this.project.getConvention().findByType(JavaApplication.class);
|
||||
if (javaApplication != null && javaApplication.getMainClassName() != null) {
|
||||
return javaApplication.getMainClassName();
|
||||
}
|
||||
return resolveMainClass();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -37,6 +38,7 @@ import org.gradle.api.java.archives.Attributes;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.specs.Specs;
|
||||
import org.gradle.api.tasks.WorkResult;
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.util.PatternSet;
|
||||
|
||||
@@ -93,7 +95,7 @@ class BootArchiveSupport {
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar) {
|
||||
CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), jar.isPreserveFileTimestamps(),
|
||||
CopyAction copyAction = new BootZipCopyAction(getOutputLocation(jar), jar.isPreserveFileTimestamps(),
|
||||
isUsingDefaultLoader(jar), this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(),
|
||||
this.launchScript, this.compressionResolver, jar.getMetadataCharset());
|
||||
if (!jar.isReproducibleFileOrder()) {
|
||||
@@ -102,6 +104,28 @@ class BootArchiveSupport {
|
||||
return new ReproducibleOrderingCopyAction(copyAction);
|
||||
}
|
||||
|
||||
private static File getOutputLocation(AbstractArchiveTask task) {
|
||||
try {
|
||||
Method method = findMethod(task.getClass(), "getArchiveFile");
|
||||
if (method != null) {
|
||||
return (File) method.invoke(task);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
return task.getArchivePath();
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> type, String name) {
|
||||
for (Method candidate : type.getMethods()) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isUsingDefaultLoader(Jar jar) {
|
||||
return DEFAULT_LAUNCHER_CLASSES.contains(jar.getManifest().getAttributes().get("Main-Class"));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.gradle.tasks.bundling;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -50,11 +51,32 @@ public class LaunchScriptConfiguration implements Serializable {
|
||||
|
||||
LaunchScriptConfiguration(AbstractArchiveTask archiveTask) {
|
||||
Project project = archiveTask.getProject();
|
||||
putIfMissing(this.properties, "initInfoProvides", archiveTask.getBaseName());
|
||||
putIfMissing(this.properties, "initInfoShortDescription", removeLineBreaks(project.getDescription()),
|
||||
archiveTask.getBaseName());
|
||||
putIfMissing(this.properties, "initInfoDescription", augmentLineBreaks(project.getDescription()),
|
||||
archiveTask.getBaseName());
|
||||
String baseName = getArchiveBaseName(archiveTask);
|
||||
putIfMissing(this.properties, "initInfoProvides", baseName);
|
||||
putIfMissing(this.properties, "initInfoShortDescription", removeLineBreaks(project.getDescription()), baseName);
|
||||
putIfMissing(this.properties, "initInfoDescription", augmentLineBreaks(project.getDescription()), baseName);
|
||||
}
|
||||
|
||||
private static String getArchiveBaseName(AbstractArchiveTask task) {
|
||||
try {
|
||||
Method method = findMethod(task.getClass(), "getArchiveBaseName");
|
||||
if (method != null) {
|
||||
return (String) method.invoke(task);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
return task.getBaseName();
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> type, String name) {
|
||||
for (Method candidate : type.getMethods()) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.boot.gradle.testkit.GradleBuildExtension;
|
||||
public final class GradleCompatibilityExtension implements TestTemplateInvocationContextProvider {
|
||||
|
||||
private static final List<String> GRADLE_VERSIONS = Arrays.asList("default", "5.0", "5.1.1", "5.2.1", "5.3.1",
|
||||
"5.4.1", "5.5.1", "5.6.4");
|
||||
"5.4.1", "5.5.1", "5.6.4", "6.0");
|
||||
|
||||
@Override
|
||||
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
|
||||
|
||||
@@ -20,7 +20,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension;
|
||||
import org.gradle.api.plugins.ApplicationPlugin;
|
||||
import org.gradle.api.plugins.JavaApplication;
|
||||
import org.gradle.testfixtures.ProjectBuilder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -51,9 +52,10 @@ class MainClassConventionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void mainClassNameProjectPropertyIsUsed() throws Exception {
|
||||
this.project.getExtensions().getByType(ExtraPropertiesExtension.class).set("mainClassName",
|
||||
"com.example.MainClass");
|
||||
void javaApplicationExtensionMainClassNameIsUsed() throws Exception {
|
||||
this.project.getPlugins().apply(ApplicationPlugin.class);
|
||||
JavaApplication extension = this.project.getExtensions().findByType(JavaApplication.class);
|
||||
extension.setMainClassName("com.example.MainClass");
|
||||
assertThat(this.convention.call()).isEqualTo("com.example.MainClass");
|
||||
}
|
||||
|
||||
@@ -67,8 +69,9 @@ class MainClassConventionTests {
|
||||
|
||||
@Test
|
||||
void springBootExtensionMainClassNameIsUsedInPreferenceToMainClassNameProjectProperty() throws Exception {
|
||||
this.project.getExtensions().getByType(ExtraPropertiesExtension.class).set("mainClassName",
|
||||
"com.example.ProjectPropertyMainClass");
|
||||
this.project.getPlugins().apply(ApplicationPlugin.class);
|
||||
JavaApplication javaApplication = this.project.getExtensions().findByType(JavaApplication.class);
|
||||
javaApplication.setMainClassName("com.example.JavaApplicationMainClass");
|
||||
SpringBootExtension extension = this.project.getExtensions().create("springBoot", SpringBootExtension.class,
|
||||
this.project);
|
||||
extension.setMainClassName("com.example.SpringBootExtensionMainClass");
|
||||
|
||||
Reference in New Issue
Block a user