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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user