diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/AotContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/aot/AotContextLoader.java index 25d649807321..9c0d21368585 100644 --- a/spring-test/src/main/java/org/springframework/test/context/aot/AotContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/aot/AotContextLoader.java @@ -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. @@ -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; @@ -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. * *

{@code AotContextLoader} is an extension of the {@link SmartContextLoader} * SPI that allows a context loader to optionally provide ahead-of-time (AOT) @@ -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. + *

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, @@ -65,13 +90,23 @@ public interface AotContextLoader extends SmartContextLoader { * throw new ContextLoadException(context, ex); * } * + *

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 @@ -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 initializer) throws Exception; diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java index 7d81e97629d1..953aa1094ff0 100644 --- a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java +++ b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java @@ -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. @@ -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 { @@ -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; } diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java index 4cc99d69a427..a06130c4ae88 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java @@ -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. @@ -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; @@ -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()) { @@ -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. + *

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. diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java index 17336b18bbdf..e1dbb4d62047 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java @@ -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. @@ -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; @@ -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}. + *

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); } diff --git a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java index f57c086cfc80..e6ec32871a93 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java @@ -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. @@ -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; @@ -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}. + *

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); } diff --git a/spring-test/src/test/java/org/springframework/test/context/support/AnnotationConfigContextLoaderTests.java b/spring-test/src/test/java/org/springframework/test/context/support/AnnotationConfigContextLoaderTests.java index 45dc4b47fabb..13690ac37386 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/AnnotationConfigContextLoaderTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/AnnotationConfigContextLoaderTests.java @@ -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. @@ -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, diff --git a/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java b/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java index 4da0bd8a3cd5..9fe85897f81c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java @@ -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. @@ -178,6 +178,7 @@ void loadContextWithConfigurationClassWithoutRefresh() throws Exception { assertApplicationContextLoadsForAotProcessing(mergedConfig, "ConfigClassTestCase.Config"); } + @SuppressWarnings("removal") private void assertApplicationContextLoadsForAotProcessing(MergedContextConfiguration mergedConfig, String expectedBeanDefName) throws Exception {