diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java index 466a827c091..c8116f62789 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java @@ -35,6 +35,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.aot.AbstractAotProcessor; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; @@ -105,6 +106,12 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, private void replaceDefinition(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata, boolean enforceExistingDefinition) { + // NOTE: This method supports 3 distinct scenarios which must be accounted for. + // + // 1) JVM runtime + // 2) AOT processing + // 3) AOT runtime + if (!(beanFactory instanceof BeanDefinitionRegistry registry)) { throw new IllegalStateException("Cannot process bean override with a BeanFactory " + "that doesn't implement BeanDefinitionRegistry: " + beanFactory.getClass().getName()); @@ -147,12 +154,24 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, if (existingBeanDefinition != null) { // Validate the existing bean definition. + // + // Applies during "JVM runtime", "AOT processing", and "AOT runtime". validateBeanDefinition(beanFactory, beanName); } + else if (Boolean.getBoolean(AbstractAotProcessor.AOT_PROCESSING)) { + // There was no existing bean definition, but during "AOT processing" we + // do not register the "pseudo" bean definition since our AOT support + // cannot automatically convert that to a functional bean definition for + // use at "AOT runtime". Furthermore, by not registering a bean definition + // for a nonexistent bean, we allow the "JVM runtime" and "AOT runtime" + // to operate the same in the following else-block. + } else { // There was no existing bean definition, so we register the "pseudo" bean // definition to ensure that a suitable bean definition exists for the given // bean name for proper autowiring candidate resolution. + // + // Applies during "JVM runtime" and "AOT runtime". registry.registerBeanDefinition(beanName, pseudoBeanDefinition); } @@ -163,6 +182,11 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, // Now we have an instance (the override) that we can register. At this stage, we don't // expect a singleton instance to be present. If for some reason a singleton instance // already exists, the following will throw an exception. + // + // As a bonus, by manually registering a singleton during "AOT processing", we allow + // GenericApplicationContext's preDetermineBeanType() method to transparently register + // runtime hints for a proxy generated by the above createOverride() invocation -- + // for example, when @MockitoBean creates a mock based on a JDK dynamic proxy. beanFactory.registerSingleton(beanName, override); } diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java index 7695515c8df..e5fa0317f83 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java @@ -155,7 +155,6 @@ class AotIntegrationTests extends AbstractAotTests { runEndToEndTests(testClasses, false); } - @Disabled("Comment out to run Bean Override integration tests in AOT mode") @Test void endToEndTestsForBeanOverrides() { List> testClasses = createTestClassScanner()