Skip to content

Commit

Permalink
Introduce RuntimeHints support in AotContextLoader
Browse files Browse the repository at this point in the history
This commit introduces a new loadContextForAotProcessing(...) variant
in AotContextLoader which accepts a RuntimeHints argument. This new
method is an interface default method which delegates to the existing
loadContextForAotProcessing(MergedContextConfiguration) variant for
backward compatibility.

In addition, the original loadContextForAotProcessing(...) variant is
now deprecated and has been converted to an interface default method
which throws an UnsupportedOperationException.

Note, however, that the framework now only invokes the new
loadContextForAotProcessing(...) variant within TestContextAotGenerator.

Closes spring-projectsgh-34513
  • Loading branch information
sbrannen committed Feb 28, 2025
1 parent c5bb842 commit 6898932
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
Expand All @@ -16,6 +16,7 @@

package org.springframework.test.context.aot;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
Expand All @@ -25,9 +26,9 @@

/**
* Strategy interface for loading an {@link ApplicationContext} for build-time
* {@linkplain #loadContextForAotProcessing AOT processing} as well as run-time
* {@linkplain #loadContextForAotRuntime AOT execution} for an integration test
* managed by the Spring TestContext Framework.
* {@linkplain #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* AOT processing} as well as run-time {@linkplain #loadContextForAotRuntime
* AOT execution} for an integration test managed by the Spring TestContext Framework.
*
* <p>{@code AotContextLoader} is an extension of the {@link SmartContextLoader}
* SPI that allows a context loader to optionally provide ahead-of-time (AOT)
Expand All @@ -42,6 +43,30 @@
*/
public interface AotContextLoader extends SmartContextLoader {

/**
* Load a new {@link ApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}, configure the context,
* and return the context.
* <p>The default implementation of this method throws an
* {@link UnsupportedOperationException}. Note, however, that the framework
* invokes {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)}
* as of Spring Framework 6.2.4.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @see #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* @see #loadContextForAotRuntime(MergedContextConfiguration, ApplicationContextInitializer)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
default ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception {
throw new UnsupportedOperationException(
"Invoke loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints) instead");
}

/**
* Load a new {@link ApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}, configure the context,
Expand All @@ -65,13 +90,23 @@ public interface AotContextLoader extends SmartContextLoader {
* throw new ContextLoadException(context, ex);
* }
* </pre>
* <p>For backward compatibility, the default implementation of this method
* delegates to {@link #loadContextForAotProcessing(MergedContextConfiguration)}.
* Note, however, that the framework only invokes this method as of Spring
* Framework 6.2.4.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @since 6.2.4
* @see #loadContextForAotRuntime(MergedContextConfiguration, ApplicationContextInitializer)
*/
ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception;
default ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {

return loadContextForAotProcessing(mergedConfig);
}

/**
* Load a new {@link ApplicationContext} for AOT run-time execution based on
Expand All @@ -98,7 +133,7 @@ public interface AotContextLoader extends SmartContextLoader {
* be applied to the context in order to recreate bean definitions
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @see #loadContextForAotProcessing(MergedContextConfiguration)
* @see #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
ApplicationContext loadContextForAotRuntime(MergedContextConfiguration mergedConfig,
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) throws Exception;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -331,7 +331,7 @@ ClassName processAheadOfTime(MergedContextConfiguration mergedConfig,
* create {@link GenericApplicationContext GenericApplicationContexts}.
* @throws TestContextAotException if an error occurs while loading the application
* context or if one of the prerequisites is not met
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
private GenericApplicationContext loadContextForAotProcessing(
MergedContextConfiguration mergedConfig) throws TestContextAotException {
Expand All @@ -345,7 +345,7 @@ private GenericApplicationContext loadContextForAotProcessing(

if (contextLoader instanceof AotContextLoader aotContextLoader) {
try {
ApplicationContext context = aotContextLoader.loadContextForAotProcessing(mergedConfig);
ApplicationContext context = aotContextLoader.loadContextForAotProcessing(mergedConfig, this.runtimeHints);
if (context instanceof GenericApplicationContext gac) {
return gac;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
Expand All @@ -19,6 +19,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
Expand Down Expand Up @@ -225,8 +226,13 @@ public final ApplicationContext loadContext(MergedContextConfiguration mergedCon
* {@code ApplicationContext} from the supplied merged context configuration
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception {
AotContextLoader loader = getAotContextLoader(mergedConfig);
if (logger.isTraceEnabled()) {
Expand All @@ -236,6 +242,33 @@ public final ApplicationContext loadContextForAotProcessing(MergedContextConfigu
return loader.loadContextForAotProcessing(mergedConfig);
}

/**
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
* an {@link ApplicationContext} for AOT processing.
* <p>Delegation is based on explicit knowledge of the implementations of the
* default loaders. See {@link #loadContext(MergedContextConfiguration)} for
* details.
* @param mergedConfig the merged context configuration to use to load the application context
* @param runtimeHints the runtime hints
* @return a new application context
* @throws IllegalArgumentException if the supplied merged configuration is {@code null}
* @throws IllegalStateException if neither candidate loader is capable of loading an
* {@code ApplicationContext} from the supplied merged context configuration
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {

AotContextLoader loader = getAotContextLoader(mergedConfig);
if (logger.isTraceEnabled()) {
logger.trace("Delegating to %s to load context for AOT processing for %s"
.formatted(name(loader), mergedConfig));
}
return loader.loadContextForAotProcessing(mergedConfig, runtimeHints);
}

/**
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
* an {@link ApplicationContext} for AOT run-time execution.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
Expand All @@ -21,6 +21,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
Expand Down Expand Up @@ -126,10 +127,41 @@ public final ApplicationContext loadContext(MergedContextConfiguration mergedCon
* @throws Exception if context loading failed
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig)
throws Exception {

return loadContext(mergedConfig, true);
}

/**
* Load a {@link GenericApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}.
* <p>In contrast to {@link #loadContext(MergedContextConfiguration)}, this
* method does not
* {@linkplain org.springframework.context.ConfigurableApplicationContext#refresh()
* refresh} the {@code ApplicationContext} or
* {@linkplain org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
* register a JVM shutdown hook} for it. Otherwise, this method implements
* behavior identical to {@link #loadContext(MergedContextConfiguration)}.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new application context
* @throws Exception if context loading failed
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {

return loadContext(mergedConfig, true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
Expand All @@ -20,6 +20,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
Expand Down Expand Up @@ -120,10 +121,41 @@ public final ApplicationContext loadContext(MergedContextConfiguration mergedCon
* @throws Exception if context loading failed
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final GenericWebApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig)
throws Exception {

return loadContext(mergedConfig, true);
}

/**
* Load a {@link GenericWebApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}.
* <p>In contrast to {@link #loadContext(MergedContextConfiguration)}, this
* method does not
* {@linkplain org.springframework.context.ConfigurableApplicationContext#refresh()
* refresh} the {@code ApplicationContext} or
* {@linkplain org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
* register a JVM shutdown hook} for it. Otherwise, this method implements
* behavior identical to {@link #loadContext(MergedContextConfiguration)}.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new web application context
* @throws Exception if context loading failed
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final GenericWebApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {

return loadContext(mergedConfig, true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -74,6 +74,7 @@ void loadContextRefreshesContext() throws Exception {
* @since 6.0
*/
@Test
@SuppressWarnings("removal")
void loadContextForAotProcessingDoesNotRefreshContext() throws Exception {
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(
AnnotatedFooConfigInnerClassTestCase.class, EMPTY_STRING_ARRAY,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -178,6 +178,7 @@ void loadContextWithConfigurationClassWithoutRefresh() throws Exception {
assertApplicationContextLoadsForAotProcessing(mergedConfig, "ConfigClassTestCase.Config");
}

@SuppressWarnings("removal")
private void assertApplicationContextLoadsForAotProcessing(MergedContextConfiguration mergedConfig,
String expectedBeanDefName) throws Exception {

Expand Down

0 comments on commit 6898932

Please sign in to comment.