diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java b/build-plugin/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java
index a96ace27fc82..8fd4c68c252c 100644
--- a/build-plugin/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java
@@ -195,4 +195,39 @@ protected String buildLog(File project) {
return contentOf(new File(project, "target/build.log"));
}
+ @TestTemplate
+ void whenAotRunsWithDevtoolsInClasspathItIsExcluded(MavenBuild mavenBuild) {
+ mavenBuild.project("aot-exclude-devtools").goals("package").execute((project) -> {
+ // The test passes if the build completes successfully.
+ // If devtools were included, the AOT processing would fail because devtools
+ // uses features (like class proxies) that are not compatible with native
+ // images.
+ Path aotDirectory = project.toPath().resolve("target/spring-aot/main");
+ assertThat(aotDirectory).exists();
+ // Verify that source files were generated, indicating successful AOT
+ // processing
+ Path sourcesDirectory = aotDirectory.resolve("sources");
+ assertThat(sourcesDirectory).exists();
+ assertThat(collectRelativePaths(sourcesDirectory)).isNotEmpty();
+ });
+ }
+
+ @TestTemplate
+ void whenTestAotRunsWithDevtoolsInClasspathItIsExcluded(MavenBuild mavenBuild) {
+ mavenBuild.project("aot-test-exclude-devtools").goals("process-test-classes").execute((project) -> {
+ // The test passes if the build completes successfully.
+ // If devtools were included, the test AOT processing would fail because
+ // devtools
+ // uses features (like class proxies) that are not compatible with native
+ // images.
+ Path aotDirectory = project.toPath().resolve("target/spring-aot/test");
+ assertThat(aotDirectory).exists();
+ // Verify that source files were generated, indicating successful AOT
+ // processing
+ Path sourcesDirectory = aotDirectory.resolve("sources");
+ assertThat(sourcesDirectory).exists();
+ assertThat(collectRelativePaths(sourcesDirectory)).isNotEmpty();
+ });
+ }
+
}
diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/pom.xml b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/pom.xml
new file mode 100644
index 000000000000..f8a155c6dadc
--- /dev/null
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+ org.springframework.boot.maven.it
+ aot-exclude-devtools
+ 0.0.1.BUILD-SNAPSHOT
+
+ UTF-8
+ @java.version@
+ @java.version@
+
+
+
+
+ @project.groupId@
+ @project.artifactId@
+ @project.version@
+
+
+
+ process-aot
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ @project.version@
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ @project.version@
+ true
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ @jakarta-servlet.version@
+ provided
+
+
+
\ No newline at end of file
diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/src/main/java/org/test/SampleApplication.java b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/src/main/java/org/test/SampleApplication.java
new file mode 100644
index 000000000000..6620ac4a2d82
--- /dev/null
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-exclude-devtools/src/main/java/org/test/SampleApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012-present 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.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/pom.xml b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/pom.xml
new file mode 100644
index 000000000000..6db8ade0eac2
--- /dev/null
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+ org.springframework.boot.maven.it
+ aot-test-exclude-devtools
+ 0.0.1.BUILD-SNAPSHOT
+
+ UTF-8
+ @java.version@
+ @java.version@
+
+
+
+
+ @project.groupId@
+ @project.artifactId@
+ @project.version@
+
+
+
+ process-test-aot
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ @project.version@
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ @project.version@
+ true
+
+
+ org.springframework.boot
+ spring-boot-test
+ @project.version@
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ @junit-jupiter.version@
+ test
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ @jakarta-servlet.version@
+ provided
+
+
+
\ No newline at end of file
diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/main/java/org/test/SampleApplication.java b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/main/java/org/test/SampleApplication.java
new file mode 100644
index 000000000000..6620ac4a2d82
--- /dev/null
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/main/java/org/test/SampleApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012-present 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.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/test/java/org/test/SampleApplicationTests.java b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/test/java/org/test/SampleApplicationTests.java
new file mode 100644
index 000000000000..cc23c3e14674
--- /dev/null
+++ b/build-plugin/spring-boot-maven-plugin/src/intTest/projects/aot-test-exclude-devtools/src/test/java/org/test/SampleApplicationTests.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012-present 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.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SampleApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
\ No newline at end of file
diff --git a/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java b/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java
index c6c940e4e2b7..524594db73ed 100644
--- a/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java
+++ b/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java
@@ -123,7 +123,7 @@ private String[] getAotArguments(String applicationClass) {
private URL[] getClassPath() throws Exception {
File[] directories = new File[] { this.classesDirectory, this.generatedClasses };
- return getClassPath(directories, new ExcludeTestScopeArtifactFilter());
+ return getClassPath(directories, new ExcludeTestScopeArtifactFilter(), DEVTOOLS_EXCLUDE_FILTER);
}
private RunArguments resolveArguments() {
diff --git a/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessTestAotMojo.java b/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessTestAotMojo.java
index 8cf523bdf114..6b3cf7929eb1 100644
--- a/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessTestAotMojo.java
+++ b/build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessTestAotMojo.java
@@ -148,7 +148,7 @@ private String[] getAotArguments() {
protected URL[] getClassPath(boolean includeJUnitPlatformLauncher) throws Exception {
File[] directories = new File[] { this.testClassesDirectory, this.generatedTestClasses, this.classesDirectory,
this.generatedClasses };
- URL[] classPath = getClassPath(directories);
+ URL[] classPath = getClassPath(directories, DEVTOOLS_EXCLUDE_FILTER);
if (!includeJUnitPlatformLauncher || this.project.getArtifactMap()
.containsKey(JUNIT_PLATFORM_GROUP_ID + ":" + JUNIT_PLATFORM_LAUNCHER_ARTIFACT_ID)) {
return classPath;