From 94fc407e07ca8d47a612dd96370343655029ad99 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Feb 2026 17:33:21 +0000 Subject: [PATCH] Ignore unloadable includes rather than failing Fixes gh-48981 --- .../filter/annotation/TypeIncludes.java | 8 ++- .../filter/annotation/TypeIncludesTests.java | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 core/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/annotation/TypeIncludesTests.java diff --git a/core/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/annotation/TypeIncludes.java b/core/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/annotation/TypeIncludes.java index 20858a977d9..a9c7148693f 100644 --- a/core/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/annotation/TypeIncludes.java +++ b/core/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/annotation/TypeIncludes.java @@ -27,10 +27,13 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.core.io.UrlResource; +import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -49,6 +52,8 @@ final class TypeIncludes implements Iterable> { private static final String COMMENT_START = "#"; + private static final Log logger = LogFactory.getLog(TypeIncludes.class); + private final Set> includes; private TypeIncludes(Set> includes) { @@ -111,7 +116,8 @@ final class TypeIncludes implements Iterable> { includes.add(ClassUtils.forName(includeName, classLoader)); } catch (Exception ex) { - throw new IllegalArgumentException("Failed to load include '" + includeName + "' declared in " + url); + logger.debug(LogMessage.format("Include '%s' declared in '%s' could not be loaded and has been ignored", + includeName, url), ex); } } return includes; diff --git a/core/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/annotation/TypeIncludesTests.java b/core/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/annotation/TypeIncludesTests.java new file mode 100644 index 00000000000..a67eb8e5ed8 --- /dev/null +++ b/core/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/annotation/TypeIncludesTests.java @@ -0,0 +1,56 @@ +/* + * 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.springframework.boot.test.context.filter.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.testsupport.classpath.resources.WithResource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link TypeIncludes}. + * + * @author Moritz Halbritter + */ +class TypeIncludesTests { + + private static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.test.context.filter.annotation.TypeIncludesTests$TestAnnotation.includes"; + + @Test + @WithResource(name = IMPORTS_FILE, content = """ + org.springframework.boot.test.context.filter.annotation.TypeIncludesTests + org.springframework.boot.test.context.filter.annotation.DoesNotExist + """) + void loadReadsFromClasspathFileIgnoringNonExistentIncludes() { + TypeIncludes candidates = TypeIncludes.load(TestAnnotation.class, + Thread.currentThread().getContextClassLoader()); + assertThat(candidates).containsExactly(TypeIncludesTests.class); + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + public @interface TestAnnotation { + + } + +}