diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java b/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java
new file mode 100644
index 00000000000..5950c7db5a5
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-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.context.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+
+/**
+ * Indicates that one or more {@link RuntimeHintsRegistrar} implementations should be processed.
+ * 
Unlike declaring {@link RuntimeHintsRegistrar} as {@code spring.factories},
+ * {@code @ImportRuntimeHints} allows for more flexible use cases where registrations are only
+ * processed if the annotated configuration class or bean method is considered by the
+ * application context.
+ *
+ * @author Brian Clozel
+ * @since 6.0
+ * @see org.springframework.aot.hint.RuntimeHints
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ImportRuntimeHints {
+
+	/**
+	 * {@link RuntimeHintsRegistrar} implementations to process.
+	 */
+	Class extends RuntimeHintsRegistrar>[] value();
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java
index 5e800e0a8ec..5159e21cb62 100644
--- a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java
+++ b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java
@@ -150,6 +150,7 @@ public class ApplicationContextAotGenerator {
 		for (String ppName : postProcessorNames) {
 			postProcessors.add(beanFactory.getBean(ppName, AotContributingBeanFactoryPostProcessor.class));
 		}
+		postProcessors.add(new RuntimeHintsPostProcessor());
 		sortPostProcessors(postProcessors, beanFactory);
 		return postProcessors;
 	}
diff --git a/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java b/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java
new file mode 100644
index 00000000000..c28dea2b0f8
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2002-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.context.generator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor;
+import org.springframework.beans.factory.generator.BeanFactoryContribution;
+import org.springframework.beans.factory.generator.BeanFactoryInitialization;
+import org.springframework.context.annotation.ImportRuntimeHints;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.core.log.LogMessage;
+import org.springframework.lang.Nullable;
+
+/**
+ * AOT {@code BeanFactoryPostProcessor} that processes {@link RuntimeHintsRegistrar} implementations
+ * declared as {@code spring.factories} or using {@link ImportRuntimeHints @ImportRuntimeHints} annotated
+ * configuration classes or bean methods.
+ * 
This processor is registered by default in the {@link ApplicationContextAotGenerator} as it is
+ * only useful in an AOT context.
+ *
+ * @author Brian Clozel
+ * @see ApplicationContextAotGenerator
+ */
+class RuntimeHintsPostProcessor implements AotContributingBeanFactoryPostProcessor {
+
+	private static final Log logger = LogFactory.getLog(RuntimeHintsPostProcessor.class);
+
+	@Override
+	public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
+		ClassLoader beanClassLoader = beanFactory.getBeanClassLoader();
+		List registrars =
+				new ArrayList<>(SpringFactoriesLoader.loadFactories(RuntimeHintsRegistrar.class, beanClassLoader));
+		Arrays.stream(beanFactory.getBeanNamesForAnnotation(ImportRuntimeHints.class)).forEach(beanDefinitionName -> {
+			ImportRuntimeHints importRuntimeHints = beanFactory.findAnnotationOnBean(beanDefinitionName, ImportRuntimeHints.class);
+			if (importRuntimeHints != null) {
+				Class extends RuntimeHintsRegistrar>[] registrarClasses = importRuntimeHints.value();
+				for (Class extends RuntimeHintsRegistrar> registrarClass : registrarClasses) {
+					logger.trace(LogMessage.format("Loaded [%s] registrar from annotated bean [%s]", registrarClass.getCanonicalName(), beanDefinitionName));
+					RuntimeHintsRegistrar registrar = BeanUtils.instantiateClass(registrarClass);
+					registrars.add(registrar);
+				}
+			}
+		});
+		return new RuntimeHintsRegistrarContribution(registrars, beanClassLoader);
+	}
+
+
+	static class RuntimeHintsRegistrarContribution implements BeanFactoryContribution {
+
+		private final List registrars;
+
+		@Nullable
+		private final ClassLoader beanClassLoader;
+
+		RuntimeHintsRegistrarContribution(List registrars, @Nullable ClassLoader beanClassLoader) {
+			this.registrars = registrars;
+			this.beanClassLoader = beanClassLoader;
+		}
+
+		@Override
+		public void applyTo(BeanFactoryInitialization initialization) {
+			this.registrars.forEach(registrar -> {
+				logger.trace(LogMessage.format("Processing RuntimeHints contribution from [%s]", registrar.getClass().getCanonicalName()));
+				registrar.registerHints(initialization.generatedTypeContext().runtimeHints(), this.beanClassLoader);
+			});
+		}
+	}
+
+}
diff --git a/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java
new file mode 100644
index 00000000000..cfd3bfe8699
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2002-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.context.generator;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.aot.generator.DefaultGeneratedTypeContext;
+import org.springframework.aot.generator.GeneratedType;
+import org.springframework.aot.hint.ResourceBundleHint;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import org.springframework.beans.BeanInstantiationException;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportRuntimeHints;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.javapoet.ClassName;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Tests for {@link RuntimeHintsPostProcessor}.
+ *
+ * @author Brian Clozel
+ */
+class RuntimeHintsPostProcessorTests {
+
+	private DefaultGeneratedTypeContext generationContext;
+
+	private ApplicationContextAotGenerator generator;
+
+	@BeforeEach
+	void setup() {
+		this.generationContext = createGenerationContext();
+		this.generator = new ApplicationContextAotGenerator();
+	}
+
+	@Test
+	void shouldProcessRegistrarOnConfiguration() {
+		GenericApplicationContext applicationContext = createContext(ConfigurationWithHints.class);
+		this.generator.generateApplicationContext(applicationContext, this.generationContext);
+		assertThatSampleRegistrarContributed();
+	}
+
+	@Test
+	void shouldProcessRegistrarOnBeanMethod() {
+		GenericApplicationContext applicationContext = createContext(ConfigurationWithBeanDeclaringHints.class);
+		this.generator.generateApplicationContext(applicationContext, this.generationContext);
+		assertThatSampleRegistrarContributed();
+	}
+
+	@Test
+	void shouldProcessRegistrarInSpringFactory() {
+		GenericApplicationContext applicationContext = createContext();
+		applicationContext.setClassLoader(new TestSpringFactoriesClassLoader());
+		this.generator.generateApplicationContext(applicationContext, this.generationContext);
+		assertThatSampleRegistrarContributed();
+	}
+
+	@Test
+	void shouldRejectRuntimeHintsRegistrarWithoutDefaultConstructor() {
+		GenericApplicationContext applicationContext = createContext(ConfigurationWithIllegalRegistrar.class);
+		assertThatThrownBy(() -> this.generator.generateApplicationContext(applicationContext, this.generationContext))
+				.isInstanceOf(BeanInstantiationException.class);
+	}
+
+	private void assertThatSampleRegistrarContributed() {
+		Stream bundleHints = this.generationContext.runtimeHints().resources().resourceBundles();
+		assertThat(bundleHints).anyMatch(bundleHint -> "sample".equals(bundleHint.getBaseName()));
+	}
+
+	private GenericApplicationContext createContext(Class>... configClasses) {
+		GenericApplicationContext applicationContext = new GenericApplicationContext();
+		AnnotationConfigUtils.registerAnnotationConfigProcessors(applicationContext);
+		for (Class> configClass : configClasses) {
+			applicationContext.registerBeanDefinition(configClass.getSimpleName(), new RootBeanDefinition(configClass));
+		}
+		applicationContext.registerBeanDefinition("runtimeHintsPostProcessor",
+				BeanDefinitionBuilder.rootBeanDefinition(RuntimeHintsPostProcessor.class, RuntimeHintsPostProcessor::new).getBeanDefinition());
+		return applicationContext;
+	}
+
+	private DefaultGeneratedTypeContext createGenerationContext() {
+		ClassName mainGeneratedType = ClassName.get("com.example", "Test");
+		return new DefaultGeneratedTypeContext(mainGeneratedType.packageName(), packageName ->
+				GeneratedType.of(ClassName.get(packageName, mainGeneratedType.simpleName())));
+	}
+
+
+	@ImportRuntimeHints(SampleRuntimeHintsRegistrar.class)
+	@Configuration(proxyBeanMethods = false)
+	static class ConfigurationWithHints {
+
+	}
+
+	@Configuration(proxyBeanMethods = false)
+	static class ConfigurationWithBeanDeclaringHints {
+
+		@Bean
+		@ImportRuntimeHints(SampleRuntimeHintsRegistrar.class)
+		SampleBean sampleBean() {
+			return new SampleBean();
+		}
+
+	}
+
+	public static class SampleRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
+
+		@Override
+		public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+			hints.resources().registerResourceBundle("sample");
+		}
+
+	}
+
+	static class SampleBean {
+
+	}
+
+	@ImportRuntimeHints(IllegalRuntimeHintsRegistrar.class)
+	@Configuration(proxyBeanMethods = false)
+	static class ConfigurationWithIllegalRegistrar {
+
+	}
+
+	public static class IllegalRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
+
+		public IllegalRuntimeHintsRegistrar(String arg) {
+
+		}
+
+		@Override
+		public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+			hints.resources().registerResourceBundle("sample");
+		}
+
+	}
+
+
+	private static class TestSpringFactoriesClassLoader extends URLClassLoader {
+
+		TestSpringFactoriesClassLoader() {
+			super(new URL[] {testLocation()}, RuntimeHintsPostProcessorTests.class.getClassLoader());
+		}
+
+		private static URL testLocation() {
+			try {
+				return new File("src/test/resources/org/springframework/context/annotation/runtimehints/").toURI().toURL();
+			}
+			catch (MalformedURLException ex) {
+				throw new IllegalStateException(ex);
+			}
+		}
+
+	}
+
+}
diff --git a/spring-context/src/test/resources/org/springframework/context/annotation/runtimehints/META-INF/spring.factories b/spring-context/src/test/resources/org/springframework/context/annotation/runtimehints/META-INF/spring.factories
new file mode 100644
index 00000000000..fc129cfb4a7
--- /dev/null
+++ b/spring-context/src/test/resources/org/springframework/context/annotation/runtimehints/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.context.generator.RuntimeHintsPostProcessorTests.SampleRuntimeHintsRegistrar
\ No newline at end of file
diff --git a/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java
new file mode 100644
index 00000000000..edcd06c431e
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-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.aot.hint;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * Contract for registering {@link RuntimeHints} in a static fashion.
+ * Implementations will contribute hints without any knowledge of the application context
+ * and can only use the given {@link ClassLoader} to conditionally contribute hints.
+ * 
{@code RuntimeHintsRegistrar} can be declared as {@code spring.factories} entries;
+ * the registrar will be processed as soon as its declaration is found in the classpath.
+ * A standard no-arg constructor is required for implementations.
+ *
+ * @author Brian Clozel
+ * @since 6.0
+ */
+@FunctionalInterface
+public interface RuntimeHintsRegistrar {
+
+	/**
+	 * Contribute hints to the given {@link RuntimeHints} instance.
+	 * @param hints the hints contributed so far for the application
+	 * @param classLoader the classloader, or {@code null} if even the system ClassLoader isn't accessible
+	 */
+	void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader);
+
+}