diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java
index 9668f1d891a2..8ba12ff87a57 100644
--- a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java
+++ b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java
@@ -16,14 +16,19 @@
package org.springframework.aot.hint.support;
+import java.util.Arrays;
import java.util.function.Consumer;
import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeHint;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.MergedAnnotation;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
/**
* Utility methods for runtime hints support code.
@@ -92,4 +97,25 @@ public static void registerAnnotationIfNecessary(RuntimeHints hints, MergedAnnot
}
}
+ /**
+ * Register that the supplied resource should be made available at runtime.
+ *
If the supplied resource is not a {@link ClassPathResource}, it will
+ * not be registered.
+ * @param hints the {@link RuntimeHints} instance to use
+ * @param resource the resource to register
+ * @throws IllegalArgumentException if the supplied resource does not
+ * {@linkplain Resource#exists() exist}
+ * @see ResourceHints#registerPattern(String)
+ */
+ public static void registerResource(RuntimeHints hints, Resource resource) {
+ if (resource instanceof ClassPathResource classPathResource) {
+ Assert.isTrue(resource.exists(), () -> "Resource does not exist: " + resource);
+ hints.resources().registerPattern(classPathResource.getPath());
+ }
+ }
+
+ public static void registerResources(RuntimeHints hints, Resource... resources) {
+ Arrays.stream(resources).forEach(resource -> registerResource(hints, resource));
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java b/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java
index b545bba6f7c4..df79accbc7c5 100644
--- a/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java
+++ b/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java
@@ -20,7 +20,9 @@
import java.util.List;
import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.support.RuntimeHintsUtils;
import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ActiveProfilesResolver;
import org.springframework.test.context.ContextLoader;
@@ -50,12 +52,12 @@ class StandardTestRuntimeHints implements TestRuntimeHintsRegistrar {
public void registerHints(MergedContextConfiguration mergedConfig, List> testClasses,
RuntimeHints runtimeHints, ClassLoader classLoader) {
- registerHintsForMergedContextConfiguration(runtimeHints, mergedConfig);
+ registerHintsForMergedContextConfiguration(runtimeHints, classLoader, mergedConfig);
testClasses.forEach(testClass -> registerHintsForActiveProfilesResolvers(runtimeHints, testClass));
}
private void registerHintsForMergedContextConfiguration(
- RuntimeHints runtimeHints, MergedContextConfiguration mergedConfig) {
+ RuntimeHints runtimeHints, ClassLoader classLoader, MergedContextConfiguration mergedConfig) {
// @ContextConfiguration(loader = ...)
ContextLoader contextLoader = mergedConfig.getContextLoader();
@@ -68,10 +70,10 @@ private void registerHintsForMergedContextConfiguration(
.forEach(clazz -> registerDeclaredConstructors(runtimeHints, clazz));
// @ContextConfiguration(locations = ...)
- registerClasspathResources(runtimeHints, mergedConfig.getLocations());
+ registerClasspathResources(runtimeHints, classLoader, mergedConfig.getLocations());
// @TestPropertySource(locations = ... )
- registerClasspathResources(runtimeHints, mergedConfig.getPropertySourceLocations());
+ registerClasspathResources(runtimeHints, classLoader, mergedConfig.getPropertySourceLocations());
// @WebAppConfiguration(value = ...)
if (mergedConfig instanceof WebMergedContextConfiguration webConfig) {
@@ -94,11 +96,14 @@ private void registerDeclaredConstructors(RuntimeHints runtimeHints, Class> ty
runtimeHints.reflection().registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
}
- private void registerClasspathResources(RuntimeHints runtimeHints, String... locations) {
+ private void registerClasspathResources(RuntimeHints runtimeHints, ClassLoader classLoader, String... locations) {
+ DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader);
Arrays.stream(locations)
+ // For the sake of efficiency, we still filter out locations that are not classpath: resources,
+ // even though the current implementation of RuntimeHintsUtils#registerResource handles that.
.filter(location -> location.startsWith(CLASSPATH_URL_PREFIX))
- .map(this::cleanClasspathResource)
- .forEach(runtimeHints.resources()::registerPattern);
+ .map(resourceLoader::getResource)
+ .forEach(resource -> RuntimeHintsUtils.registerResource(runtimeHints, resource));
}
private void registerClasspathResourceDirectoryStructure(RuntimeHints runtimeHints, String directory) {
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
index 0b16e4f216b9..faca617b6f0b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
@@ -28,10 +28,12 @@
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.support.RuntimeHintsUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.lang.NonNull;
@@ -148,10 +150,10 @@ public void afterTestMethod(TestContext testContext) {
@Override
public void processAheadOfTime(Class> testClass, RuntimeHints runtimeHints, ClassLoader classLoader) {
getSqlAnnotationsFor(testClass).forEach(sql ->
- registerClasspathResources(runtimeHints, getScripts(sql, testClass, null, true)));
+ registerClasspathResources(runtimeHints, classLoader, getScripts(sql, testClass, null, true)));
getSqlMethods(testClass).forEach(testMethod ->
getSqlAnnotationsFor(testMethod).forEach(sql ->
- registerClasspathResources(runtimeHints, getScripts(sql, testClass, testMethod, false))));
+ registerClasspathResources(runtimeHints, classLoader, getScripts(sql, testClass, testMethod, false))));
}
/**
@@ -390,19 +392,10 @@ private Stream getSqlMethods(Class> testClass) {
return Arrays.stream(ReflectionUtils.getUniqueDeclaredMethods(testClass, sqlMethodFilter));
}
- private void registerClasspathResources(RuntimeHints runtimeHints, String... locations) {
- Arrays.stream(locations)
- .filter(location -> location.startsWith(CLASSPATH_URL_PREFIX))
- .map(this::cleanClasspathResource)
- .forEach(runtimeHints.resources()::registerPattern);
- }
-
- private String cleanClasspathResource(String location) {
- location = location.substring(CLASSPATH_URL_PREFIX.length());
- if (!location.startsWith(SLASH)) {
- location = SLASH + location;
- }
- return location;
+ private void registerClasspathResources(RuntimeHints runtimeHints, ClassLoader classLoader, String... locations) {
+ DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader);
+ RuntimeHintsUtils.registerResources(runtimeHints,
+ TestContextResourceUtils.convertToResources(resourceLoader, locations));
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java
index 8124709a86b2..3efa0409c203 100644
--- a/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java
@@ -181,11 +181,11 @@ private static void assertRuntimeHints(RuntimeHints runtimeHints) {
).forEach(type -> assertReflectionRegistered(runtimeHints, type, INVOKE_DECLARED_CONSTRUCTORS));
// @ContextConfiguration(locations = ...)
- assertThat(resource().forResource("/org/springframework/test/context/aot/samples/xml/test-config.xml"))
+ assertThat(resource().forResource("org/springframework/test/context/aot/samples/xml/test-config.xml"))
.accepts(runtimeHints);
// @TestPropertySource(locations = ...)
- assertThat(resource().forResource("/org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests.properties"))
+ assertThat(resource().forResource("org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests.properties"))
.accepts(runtimeHints);
// @WebAppConfiguration(value = ...)
@@ -193,9 +193,9 @@ private static void assertRuntimeHints(RuntimeHints runtimeHints) {
assertThat(resource().forResource("/META-INF/web-resources/WEB-INF/views/home.jsp")).accepts(runtimeHints);
// @Sql(scripts = ...)
- assertThat(resource().forResource("/org/springframework/test/context/jdbc/schema.sql"))
+ assertThat(resource().forResource("org/springframework/test/context/jdbc/schema.sql"))
.accepts(runtimeHints);
- assertThat(resource().forResource("/org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests.test.sql"))
+ assertThat(resource().forResource("org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests.test.sql"))
.accepts(runtimeHints);
}