forked from spring-projects/spring-boot
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for invoking AOT in the Maven Plugin
This commit adds an `aot-generate` goal to the Maven Plugin that triggers AOT generation on the application. The new goal shares a number of properties with the existing `run` goal and uses the same algorithm to detect the main class to use. Closes spring-projectsgh-30525
- Loading branch information
1 parent
007dc11
commit c72e2eb
Showing
12 changed files
with
577 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
...t-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/aot.adoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[[aot]] | ||
= Optimizing Your Application at Build-Time | ||
|
||
Spring AOT inspects an application at build-time and generates an optimized version of it. | ||
Based on your `@SpringBootApplication`-annotated main class, the AOT engine generates a persistent view of the beans that are going to be contributed at runtime in a way that bean instantiation is as straightforward as possible. | ||
Additional post-processing of the factory is possible using callbacks. | ||
For instance, these are used to generate the necessary reflection configuration that GraalVM needs to initialize the context in a native image. | ||
|
||
To configure your application to use this feature, add an execution for the `aot-generate` goal, as shown in the following example: | ||
|
||
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4] | ||
---- | ||
include::../maven/aot/pom.xml[tags=aot] | ||
---- | ||
|
||
As the `BeanFactory` is fully prepared at build-time, conditions are also evaluated. | ||
This has an important difference compared to what a regular Spring Boot application does at runtime. | ||
For instance, if you want to opt-in or opt-out for certain features, you need to configure the environment used at build time to do so. | ||
The `aot-generate` goal shares a number of properties with the <<run,run goal>> for that reason. | ||
|
||
|
||
include::goals/aot-generate.adoc[leveloffset=+1] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/maven/aot/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project> | ||
<modelVersion>4.0.0</modelVersion> | ||
<artifactId>aot</artifactId> | ||
<build> | ||
<plugins> | ||
<!-- tag::aot[] --> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>aot-generate</id> | ||
<goals> | ||
<goal>aot-generate</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<!-- end::aot[] --> | ||
</plugins> | ||
</build> | ||
</project> | ||
|
||
|
76 changes: 76 additions & 0 deletions
76
...g-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotGenerateTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright 2012-2022 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 org.springframework.boot.maven; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.stream.Stream; | ||
|
||
import org.junit.jupiter.api.TestTemplate; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
/** | ||
* Integration tests for the Maven plugin's AOT support. | ||
* | ||
* @author Stephane Nicoll | ||
*/ | ||
@ExtendWith(MavenBuildExtension.class) | ||
public class AotGenerateTests { | ||
|
||
@TestTemplate | ||
void whenAotRunsSourcesAreGenerated(MavenBuild mavenBuild) { | ||
mavenBuild.project("aot").goals("package").execute((project) -> { | ||
Path aotDirectory = project.toPath().resolve("target/spring-aot/main"); | ||
assertThat(collectRelativeFileNames(aotDirectory.resolve("sources"))) | ||
.containsOnly("org/test/SampleApplication__ApplicationContextInitializer.java"); | ||
assertThat(collectRelativeFileNames(aotDirectory.resolve("resources"))) | ||
.containsOnly("META-INF/native-image/reflect-config.json"); | ||
}); | ||
} | ||
|
||
@TestTemplate | ||
void whenAotRunsSourcesAreCompiled(MavenBuild mavenBuild) { | ||
mavenBuild.project("aot").goals("package").execute((project) -> { | ||
Path classesDirectory = project.toPath().resolve("target/classes"); | ||
assertThat(collectRelativeFileNames(classesDirectory)) | ||
.contains("org/test/SampleApplication__ApplicationContextInitializer.class"); | ||
}); | ||
} | ||
|
||
@TestTemplate | ||
void whenAotRunsResourcesAreCopiedUsingProjectCoordinates(MavenBuild mavenBuild) { | ||
mavenBuild.project("aot").goals("package").execute((project) -> { | ||
Path classesDirectory = project.toPath().resolve("target/classes/META-INF/native-image"); | ||
assertThat(collectRelativeFileNames(classesDirectory)) | ||
.contains("org.springframework.boot.maven.it/aot/reflect-config.json"); | ||
}); | ||
} | ||
|
||
Stream<String> collectRelativeFileNames(Path sourceDirectory) { | ||
try { | ||
return Files.walk(sourceDirectory).filter(Files::isRegularFile) | ||
.map((path) -> path.subpath(sourceDirectory.getNameCount(), path.getNameCount()).toString()); | ||
} | ||
catch (IOException ex) { | ||
throw new IllegalStateException(ex); | ||
} | ||
} | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
...-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>org.springframework.boot.maven.it</groupId> | ||
<artifactId>aot</artifactId> | ||
<version>0.0.1.BUILD-SNAPSHOT</version> | ||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<maven.compiler.source>@java.version@</maven.compiler.source> | ||
<maven.compiler.target>@java.version@</maven.compiler.target> | ||
</properties> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>@project.groupId@</groupId> | ||
<artifactId>@project.artifactId@</artifactId> | ||
<version>@project.version@</version> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>aot-generate</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot</artifactId> | ||
<version>@project.version@</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>jakarta.servlet</groupId> | ||
<artifactId>jakarta.servlet-api</artifactId> | ||
<version>@jakarta-servlet.version@</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
</dependencies> | ||
</project> |
29 changes: 29 additions & 0 deletions
29
...-boot-maven-plugin/src/intTest/projects/aot/src/main/java/org/test/SampleApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright 2012-2020 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 org.test; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
public class SampleApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(SampleApplication.class, args); | ||
} | ||
|
||
} |
144 changes: 144 additions & 0 deletions
144
...maven-plugin/src/main/java/org/springframework/boot/maven/AbstractApplicationRunMojo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/* | ||
* Copyright 2012-2022 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 org.springframework.boot.maven; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.apache.maven.model.Resource; | ||
import org.apache.maven.plugin.MojoExecutionException; | ||
import org.apache.maven.plugin.MojoFailureException; | ||
import org.apache.maven.plugins.annotations.Parameter; | ||
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; | ||
|
||
import org.springframework.boot.loader.tools.FileUtils; | ||
|
||
/** | ||
* Base class to run a spring application. | ||
* | ||
* @author Phillip Webb | ||
* @author Stephane Nicoll | ||
* @author David Liu | ||
* @author Daniel Young | ||
* @author Dmytro Nosan | ||
* @since 6.0.0 | ||
* @see RunMojo | ||
* @see StartMojo | ||
*/ | ||
public abstract class AbstractApplicationRunMojo extends AbstractRunMojo { | ||
|
||
/** | ||
* Add maven resources to the classpath directly, this allows live in-place editing of | ||
* resources. Duplicate resources are removed from {@code target/classes} to prevent | ||
* them to appear twice if {@code ClassLoader.getResources()} is called. Please | ||
* consider adding {@code spring-boot-devtools} to your project instead as it provides | ||
* this feature and many more. | ||
* @since 1.0.0 | ||
*/ | ||
@Parameter(property = "spring-boot.run.addResources", defaultValue = "false") | ||
private boolean addResources = false; | ||
|
||
/** | ||
* Path to agent jars. NOTE: a forked process is required to use this feature. | ||
* @since 2.2.0 | ||
*/ | ||
@Parameter(property = "spring-boot.run.agents") | ||
private File[] agents; | ||
|
||
/** | ||
* Flag to say that the agent requires -noverify. | ||
* @since 1.0.0 | ||
*/ | ||
@Parameter(property = "spring-boot.run.noverify") | ||
private boolean noverify = false; | ||
|
||
/** | ||
* Flag to include the test classpath when running. | ||
* @since 1.3.0 | ||
*/ | ||
@Parameter(property = "spring-boot.run.useTestClasspath", defaultValue = "false") | ||
private Boolean useTestClasspath; | ||
|
||
@Override | ||
protected void run(File workingDirectory, String startClassName, Map<String, String> environmentVariables) | ||
throws MojoExecutionException, MojoFailureException { | ||
List<String> args = new ArrayList<>(); | ||
addAgents(args); | ||
addJvmArgs(args); | ||
addClasspath(args); | ||
args.add(startClassName); | ||
addArgs(args); | ||
run(workingDirectory, args, environmentVariables); | ||
} | ||
|
||
/** | ||
* Run with a forked VM, using the specified command line arguments. | ||
* @param workingDirectory the working directory of the forked JVM | ||
* @param args the arguments (JVM arguments and application arguments) | ||
* @param environmentVariables the environment variables | ||
* @throws MojoExecutionException in case of MOJO execution errors | ||
* @throws MojoFailureException in case of MOJO failures | ||
*/ | ||
protected abstract void run(File workingDirectory, List<String> args, Map<String, String> environmentVariables) | ||
throws MojoExecutionException, MojoFailureException; | ||
|
||
@Override | ||
protected URL[] getClassPathUrls() throws MojoExecutionException { | ||
try { | ||
List<URL> urls = new ArrayList<>(); | ||
addUserDefinedDirectories(urls); | ||
addResources(urls); | ||
addProjectClasses(urls); | ||
FilterArtifacts filters = (this.useTestClasspath ? getFilters() : getFilters(new TestArtifactFilter())); | ||
addDependencies(urls, filters); | ||
return urls.toArray(new URL[0]); | ||
} | ||
catch (IOException ex) { | ||
throw new MojoExecutionException("Unable to build classpath", ex); | ||
} | ||
} | ||
|
||
private void addAgents(List<String> args) { | ||
if (this.agents != null) { | ||
if (getLog().isInfoEnabled()) { | ||
getLog().info("Attaching agents: " + Arrays.asList(this.agents)); | ||
} | ||
for (File agent : this.agents) { | ||
args.add("-javaagent:" + agent); | ||
} | ||
} | ||
if (this.noverify) { | ||
args.add("-noverify"); | ||
} | ||
} | ||
|
||
private void addResources(List<URL> urls) throws IOException { | ||
if (this.addResources) { | ||
for (Resource resource : this.project.getResources()) { | ||
File directory = new File(resource.getDirectory()); | ||
urls.add(directory.toURI().toURL()); | ||
FileUtils.removeDuplicatesFromOutputDirectory(this.classesDirectory, directory); | ||
} | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.