Revise singleton destruction for Bean Overrides

See gh-33678
This commit is contained in:
Sam Brannen
2024-10-10 12:13:44 +02:00
parent 8d652e9c12
commit f7e32a9c78

View File

@@ -154,12 +154,6 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
}
}
// Ensure we don't have any manually registered singletons registered, since we
// register a bean override instance as a manual singleton at the end of this method.
if (beanFactory.containsSingleton(beanName)) {
destroySingleton(beanFactory, beanName);
}
if (existingBeanDefinition != null) {
// Validate the existing bean definition.
//
@@ -200,14 +194,19 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
overrideMetadata.track(override, beanFactory);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanName);
// 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.
// Now we have an instance (the override) that we can manually register as a singleton.
//
// However, we need to remove any existing singleton instance -- for example, a
// manually registered singleton or a singleton that was registered as a side effect
// of the isSingleton() check in validateBeanDefinition().
//
// 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.
if (beanFactory.containsSingleton(beanName)) {
destroySingleton(beanFactory, beanName);
}
beanFactory.registerSingleton(beanName, override);
}
@@ -339,17 +338,14 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
private static void validateBeanDefinition(ConfigurableListableBeanFactory beanFactory, String beanName) {
Assert.state(beanFactory.isSingleton(beanName),
() -> "Unable to override bean '" + beanName + "': only singleton beans can be overridden.");
// Since the isSingleton() check above may have registered a singleton as a side
// effect -- for example, for a FactoryBean -- we need to destroy the singleton,
// because we later manually register a bean override instance as a singleton.
destroySingleton(beanFactory, beanName);
}
private static void destroySingleton(ConfigurableListableBeanFactory beanFactory, String beanName) {
if (beanFactory instanceof DefaultListableBeanFactory dlbf) {
dlbf.destroySingleton(beanName);
if (!(beanFactory instanceof DefaultListableBeanFactory dlbf)) {
throw new IllegalStateException("Cannot process bean override with a BeanFactory " +
"that doesn't implement DefaultListableBeanFactory: " + beanFactory.getClass().getName());
}
dlbf.destroySingleton(beanName);
}
}