Reverse engineer documentation for Bean Override internals

This commit is contained in:
Sam Brannen 2024-09-27 14:44:55 +02:00
parent ded5c13e19
commit c3ff6cf319
2 changed files with 46 additions and 38 deletions

View File

@ -104,19 +104,6 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
} }
} }
/**
* Copy certain details of a {@link BeanDefinition} to the definition created by
* this processor for a given {@link OverrideMetadata}.
* <p>The default implementation copies the {@linkplain BeanDefinition#isPrimary()
* primary flag}, @{@linkplain BeanDefinition#isFallback() fallback flag}
* and the {@linkplain BeanDefinition#getScope() scope}.
*/
protected void copyBeanDefinitionDetails(BeanDefinition from, RootBeanDefinition to) {
to.setPrimary(from.isPrimary());
to.setFallback(from.isFallback());
to.setScope(from.getScope());
}
private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry, private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
OverrideMetadata overrideMetadata) { OverrideMetadata overrideMetadata) {
@ -132,6 +119,8 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
private void registerReplaceDefinition(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry, private void registerReplaceDefinition(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
OverrideMetadata overrideMetadata, boolean enforceExistingDefinition) { OverrideMetadata overrideMetadata, boolean enforceExistingDefinition) {
// The following is a "dummy" bean definition which should not be used to
// create an actual bean instance.
RootBeanDefinition beanDefinition = createBeanDefinition(overrideMetadata); RootBeanDefinition beanDefinition = createBeanDefinition(overrideMetadata);
String beanName = overrideMetadata.getBeanName(); String beanName = overrideMetadata.getBeanName();
String beanNameIncludingFactory; String beanNameIncludingFactory;
@ -157,22 +146,27 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
beanNameIncludingFactory = beanName; beanNameIncludingFactory = beanName;
} }
// Process existing bean definition.
if (existingBeanDefinition != null) { if (existingBeanDefinition != null) {
copyBeanDefinitionDetails(existingBeanDefinition, beanDefinition); copyBeanDefinitionProperties(existingBeanDefinition, beanDefinition);
registry.removeBeanDefinition(beanName); registry.removeBeanDefinition(beanName);
} }
// At this point, we either removed an existing bean definition above, or
// there was no bean definition to begin with. So, we register the dummy bean
// definition to ensure that a bean definition exists for the given bean name.
registry.registerBeanDefinition(beanName, beanDefinition); registry.registerBeanDefinition(beanName, beanDefinition);
Object override = overrideMetadata.createOverride(beanName, existingBeanDefinition, null); Object override = overrideMetadata.createOverride(beanName, existingBeanDefinition, null);
if (beanFactory.isSingleton(beanNameIncludingFactory)) {
// Now we have an instance (the override) that we can register.
// At this stage we don't expect a singleton instance to be present,
// and this call will throw if there is such an instance already.
beanFactory.registerSingleton(beanName, override);
}
overrideMetadata.track(override, beanFactory); overrideMetadata.track(override, beanFactory);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanNameIncludingFactory); this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanNameIncludingFactory);
if (beanFactory.isSingleton(beanNameIncludingFactory)) {
// Now we have an instance (the override) that we can register. At this
// stage we don't expect a singleton instance to be present, and this call
// will throw an exception if there is such an instance already.
beanFactory.registerSingleton(beanName, override);
}
} }
private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry, private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
@ -231,13 +225,6 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
this.overrideRegistrar.registerNameForMetadata(metadata, beanName); this.overrideRegistrar.registerNameForMetadata(metadata, beanName);
} }
RootBeanDefinition createBeanDefinition(OverrideMetadata metadata) {
RootBeanDefinition definition = new RootBeanDefinition(metadata.getBeanType().resolve());
definition.setTargetType(metadata.getBeanType());
definition.setQualifiedElement(metadata.getField());
return definition;
}
private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata metadata, private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata metadata,
boolean checkAutowiredCandidate) { boolean checkAutowiredCandidate) {
@ -272,6 +259,26 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
} }
private static RootBeanDefinition createBeanDefinition(OverrideMetadata metadata) {
RootBeanDefinition definition = new RootBeanDefinition(metadata.getBeanType().resolve());
definition.setTargetType(metadata.getBeanType());
definition.setQualifiedElement(metadata.getField());
return definition;
}
/**
* Copy the following properties of the source {@link BeanDefinition} to the
* target: the {@linkplain BeanDefinition#isPrimary() primary flag}, the
* {@linkplain BeanDefinition#isFallback() fallback flag}, and the
* {@linkplain BeanDefinition#getScope() scope}.
*/
private static void copyBeanDefinitionProperties(BeanDefinition source, RootBeanDefinition target) {
target.setPrimary(source.isPrimary());
target.setFallback(source.isFallback());
target.setScope(source.getScope());
}
static class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, static class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
PriorityOrdered { PriorityOrdered {

View File

@ -51,6 +51,7 @@ import static org.mockito.Mockito.mock;
* *
* @author Simon Baslé * @author Simon Baslé
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Sam Brannen
*/ */
class BeanOverrideBeanFactoryPostProcessorTests { class BeanOverrideBeanFactoryPostProcessorTests {
@ -200,7 +201,7 @@ class BeanOverrideBeanFactoryPostProcessorTests {
} }
@Test @Test
void allowReplaceDefinitionWhenSingletonDefinitionPresent() { void replaceBeanByNameWithMatchingBeanDefinitionWithExplicitSingletonScope() {
AnnotationConfigApplicationContext context = createContext(CaseByName.class); AnnotationConfigApplicationContext context = createContext(CaseByName.class);
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL"); RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
definition.setScope(BeanDefinition.SCOPE_SINGLETON); definition.setScope(BeanDefinition.SCOPE_SINGLETON);
@ -212,7 +213,7 @@ class BeanOverrideBeanFactoryPostProcessorTests {
} }
@Test @Test
void copyDefinitionPrimaryFallbackAndScope() { void replaceBeanByNameWithMatchingBeanDefinitionRetainsPrimaryFallbackAndScopeProperties() {
AnnotationConfigApplicationContext context = createContext(CaseByName.class); AnnotationConfigApplicationContext context = createContext(CaseByName.class);
context.getBeanFactory().registerScope("customScope", new SimpleThreadScope()); context.getBeanFactory().registerScope("customScope", new SimpleThreadScope());
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL"); RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
@ -232,22 +233,22 @@ class BeanOverrideBeanFactoryPostProcessorTests {
} }
@Test @Test
void createDefinitionShouldSetQualifierElement() { void qualifiedElementIsSetToBeanOverrideField() {
AnnotationConfigApplicationContext context = createContext(CaseByNameWithQualifier.class); AnnotationConfigApplicationContext context = createContext(CaseByNameWithQualifier.class);
context.registerBeanDefinition("descriptionBean", new RootBeanDefinition(String.class, () -> "ORIGINAL")); context.registerBeanDefinition("descriptionBean", new RootBeanDefinition(String.class, () -> "ORIGINAL"));
assertThatNoException().isThrownBy(context::refresh); assertThatNoException().isThrownBy(context::refresh);
assertThat(context.getBeanDefinition("descriptionBean")) assertThat(context.getBeanDefinition("descriptionBean"))
.isInstanceOfSatisfying(RootBeanDefinition.class, this::isTheValueField); .isInstanceOfSatisfying(RootBeanDefinition.class, this::qualifiedElementIsField);
} }
private void isTheValueField(RootBeanDefinition def) { private void qualifiedElementIsField(RootBeanDefinition def) {
assertThat(def.getQualifiedElement()).isInstanceOfSatisfying(Field.class, field -> { assertThat(def.getQualifiedElement()).isInstanceOfSatisfying(Field.class,
assertThat(field.getDeclaringClass()).isEqualTo(CaseByNameWithQualifier.class); field -> {
assertThat(field.getName()).as("annotated field name") assertThat(field.getDeclaringClass()).isEqualTo(CaseByNameWithQualifier.class);
.isEqualTo("description"); assertThat(field.getName()).as("annotated field name").isEqualTo("description");
}); });
} }
private AnnotationConfigApplicationContext createContext(Class<?> testClass) { private AnnotationConfigApplicationContext createContext(Class<?> testClass) {