Skip to content

Commit d34f176

Browse files
committed
Exclude spring-boot-devtools from AOT processing and native images in Maven
Previously, spring-boot-devtools was only excluded from native images built with Gradle but not with Maven. This inconsistency meant that Maven builds would include devtools in the AOT processing classpath and in the native image, causing build failures. This commit: - Applies DEVTOOLS_EXCLUDE_FILTER to the classpath during both main and test AOT processing in Maven plugin - Adds exclusions for devtools and docker-compose in the native-maven-plugin configuration within spring-boot-starter-parent - Ensures devtools is completely excluded from native images, not just from AOT processing The exclusion happens automatically without requiring any user configuration, ensuring that devtools is properly excluded from native images while maintaining its functionality during development. Fixes gh-32853 Signed-off-by: academey <[email protected]>
1 parent 81a4a33 commit d34f176

File tree

9 files changed

+252
-2
lines changed

9 files changed

+252
-2
lines changed

build-plugin/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,39 @@ protected String buildLog(File project) {
195195
return contentOf(new File(project, "target/build.log"));
196196
}
197197

198+
@TestTemplate
199+
void whenAotRunsWithDevtoolsInClasspathItIsExcluded(MavenBuild mavenBuild) {
200+
mavenBuild.project("aot-exclude-devtools").goals("package").execute((project) -> {
201+
// The test passes if the build completes successfully.
202+
// If devtools were included, the AOT processing would fail because devtools
203+
// uses features (like class proxies) that are not compatible with native
204+
// images.
205+
Path aotDirectory = project.toPath().resolve("target/spring-aot/main");
206+
assertThat(aotDirectory).exists();
207+
// Verify that source files were generated, indicating successful AOT
208+
// processing
209+
Path sourcesDirectory = aotDirectory.resolve("sources");
210+
assertThat(sourcesDirectory).exists();
211+
assertThat(collectRelativePaths(sourcesDirectory)).isNotEmpty();
212+
});
213+
}
214+
215+
@TestTemplate
216+
void whenTestAotRunsWithDevtoolsInClasspathItIsExcluded(MavenBuild mavenBuild) {
217+
mavenBuild.project("aot-test-exclude-devtools").goals("process-test-classes").execute((project) -> {
218+
// The test passes if the build completes successfully.
219+
// If devtools were included, the test AOT processing would fail because
220+
// devtools
221+
// uses features (like class proxies) that are not compatible with native
222+
// images.
223+
Path aotDirectory = project.toPath().resolve("target/spring-aot/test");
224+
assertThat(aotDirectory).exists();
225+
// Verify that source files were generated, indicating successful AOT
226+
// processing
227+
Path sourcesDirectory = aotDirectory.resolve("sources");
228+
assertThat(sourcesDirectory).exists();
229+
assertThat(collectRelativePaths(sourcesDirectory)).isNotEmpty();
230+
});
231+
}
232+
198233
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>org.springframework.boot.maven.it</groupId>
6+
<artifactId>aot-exclude-devtools</artifactId>
7+
<version>0.0.1.BUILD-SNAPSHOT</version>
8+
<properties>
9+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
10+
<maven.compiler.source>@java.version@</maven.compiler.source>
11+
<maven.compiler.target>@java.version@</maven.compiler.target>
12+
</properties>
13+
<build>
14+
<plugins>
15+
<plugin>
16+
<groupId>@project.groupId@</groupId>
17+
<artifactId>@project.artifactId@</artifactId>
18+
<version>@project.version@</version>
19+
<executions>
20+
<execution>
21+
<goals>
22+
<goal>process-aot</goal>
23+
</goals>
24+
</execution>
25+
</executions>
26+
</plugin>
27+
</plugins>
28+
</build>
29+
<dependencies>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot</artifactId>
33+
<version>@project.version@</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-devtools</artifactId>
38+
<version>@project.version@</version>
39+
<optional>true</optional>
40+
</dependency>
41+
<dependency>
42+
<groupId>jakarta.servlet</groupId>
43+
<artifactId>jakarta.servlet-api</artifactId>
44+
<version>@jakarta-servlet.version@</version>
45+
<scope>provided</scope>
46+
</dependency>
47+
</dependencies>
48+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present 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 org.test;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class SampleApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(SampleApplication.class, args);
27+
}
28+
29+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>org.springframework.boot.maven.it</groupId>
6+
<artifactId>aot-test-exclude-devtools</artifactId>
7+
<version>0.0.1.BUILD-SNAPSHOT</version>
8+
<properties>
9+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
10+
<maven.compiler.source>@java.version@</maven.compiler.source>
11+
<maven.compiler.target>@java.version@</maven.compiler.target>
12+
</properties>
13+
<build>
14+
<plugins>
15+
<plugin>
16+
<groupId>@project.groupId@</groupId>
17+
<artifactId>@project.artifactId@</artifactId>
18+
<version>@project.version@</version>
19+
<executions>
20+
<execution>
21+
<goals>
22+
<goal>process-test-aot</goal>
23+
</goals>
24+
</execution>
25+
</executions>
26+
</plugin>
27+
</plugins>
28+
</build>
29+
<dependencies>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot</artifactId>
33+
<version>@project.version@</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-devtools</artifactId>
38+
<version>@project.version@</version>
39+
<optional>true</optional>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-test</artifactId>
44+
<version>@project.version@</version>
45+
<scope>test</scope>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.junit.jupiter</groupId>
49+
<artifactId>junit-jupiter</artifactId>
50+
<version>@junit-jupiter.version@</version>
51+
<scope>test</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>jakarta.servlet</groupId>
55+
<artifactId>jakarta.servlet-api</artifactId>
56+
<version>@jakarta-servlet.version@</version>
57+
<scope>provided</scope>
58+
</dependency>
59+
</dependencies>
60+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present 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 org.test;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class SampleApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(SampleApplication.class, args);
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present 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 org.test;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.springframework.boot.test.context.SpringBootTest;
21+
22+
@SpringBootTest
23+
class SampleApplicationTests {
24+
25+
@Test
26+
void contextLoads() {
27+
}
28+
29+
}

build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ private String[] getAotArguments(String applicationClass) {
123123

124124
private URL[] getClassPath() throws Exception {
125125
File[] directories = new File[] { this.classesDirectory, this.generatedClasses };
126-
return getClassPath(directories, new ExcludeTestScopeArtifactFilter());
126+
return getClassPath(directories, new ExcludeTestScopeArtifactFilter(), DEVTOOLS_EXCLUDE_FILTER);
127127
}
128128

129129
private RunArguments resolveArguments() {

build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessTestAotMojo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private String[] getAotArguments() {
148148
protected URL[] getClassPath(boolean includeJUnitPlatformLauncher) throws Exception {
149149
File[] directories = new File[] { this.testClassesDirectory, this.generatedTestClasses, this.classesDirectory,
150150
this.generatedClasses };
151-
URL[] classPath = getClassPath(directories);
151+
URL[] classPath = getClassPath(directories, DEVTOOLS_EXCLUDE_FILTER);
152152
if (!includeJUnitPlatformLauncher || this.project.getArtifactMap()
153153
.containsKey(JUNIT_PLATFORM_GROUP_ID + ":" + JUNIT_PLATFORM_LAUNCHER_ARTIFACT_ID)) {
154154
return classPath;

starter/spring-boot-starter-parent/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ publishing.publications.withType(MavenPublication) {
298298
configuration {
299299
delegate.classesDirectory('${project.build.outputDirectory}')
300300
delegate.requiredVersion('22.3')
301+
exclusions {
302+
exclusion {
303+
delegate.groupId('org.springframework.boot')
304+
delegate.artifactId('spring-boot-devtools')
305+
}
306+
exclusion {
307+
delegate.groupId('org.springframework.boot')
308+
delegate.artifactId('spring-boot-docker-compose')
309+
}
310+
}
301311
}
302312
executions {
303313
execution {
@@ -342,6 +352,16 @@ publishing.publications.withType(MavenPublication) {
342352
configuration {
343353
delegate.classesDirectory('${project.build.outputDirectory}')
344354
delegate.requiredVersion('22.3')
355+
exclusions {
356+
exclusion {
357+
delegate.groupId('org.springframework.boot')
358+
delegate.artifactId('spring-boot-devtools')
359+
}
360+
exclusion {
361+
delegate.groupId('org.springframework.boot')
362+
delegate.artifactId('spring-boot-docker-compose')
363+
}
364+
}
345365
}
346366
executions {
347367
execution {

0 commit comments

Comments
 (0)