Browse Source

Skip pseudo bean definition registration during AOT processing

Prior to this commit, AOT processing failed for tests that made use of
the Bean Override feature to "override" a nonexistent bean. The reason
is that we register a "pseudo" bean definition as a placeholder for a
nonexistent bean, and our AOT support cannot automatically convert that
"pseudo" bean definition to a functional bean definition for use at AOT
runtime.

To address that, this commit skips registration of "pseudo" bean
definitions during AOT processing, and by doing so we enable the JVM
runtime and AOT runtime to operate with the same semantics.

See gh-32933
pull/33671/head
Sam Brannen 1 year ago
parent
commit
5ddeb06640
  1. 24
      spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java
  2. 1
      spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java

24
spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java

@ -35,6 +35,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator; @@ -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, @@ -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, @@ -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, @@ -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);
}

1
spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java

@ -155,7 +155,6 @@ class AotIntegrationTests extends AbstractAotTests { @@ -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<Class<?>> testClasses = createTestClassScanner()

Loading…
Cancel
Save