Skip to content

Commit c3ff6cf

Browse files
committed
Reverse engineer documentation for Bean Override internals
1 parent ded5c13 commit c3ff6cf

File tree

2 files changed

+45
-37
lines changed

2 files changed

+45
-37
lines changed

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

+34-27
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,6 @@ private void postProcessWithRegistry(ConfigurableListableBeanFactory beanFactory
104104
}
105105
}
106106

107-
/**
108-
* Copy certain details of a {@link BeanDefinition} to the definition created by
109-
* this processor for a given {@link OverrideMetadata}.
110-
* <p>The default implementation copies the {@linkplain BeanDefinition#isPrimary()
111-
* primary flag}, @{@linkplain BeanDefinition#isFallback() fallback flag}
112-
* and the {@linkplain BeanDefinition#getScope() scope}.
113-
*/
114-
protected void copyBeanDefinitionDetails(BeanDefinition from, RootBeanDefinition to) {
115-
to.setPrimary(from.isPrimary());
116-
to.setFallback(from.isFallback());
117-
to.setScope(from.getScope());
118-
}
119-
120107
private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
121108
OverrideMetadata overrideMetadata) {
122109

@@ -132,6 +119,8 @@ private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, B
132119
private void registerReplaceDefinition(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
133120
OverrideMetadata overrideMetadata, boolean enforceExistingDefinition) {
134121

122+
// The following is a "dummy" bean definition which should not be used to
123+
// create an actual bean instance.
135124
RootBeanDefinition beanDefinition = createBeanDefinition(overrideMetadata);
136125
String beanName = overrideMetadata.getBeanName();
137126
String beanNameIncludingFactory;
@@ -157,22 +146,27 @@ else if (enforceExistingDefinition) {
157146
beanNameIncludingFactory = beanName;
158147
}
159148

149+
// Process existing bean definition.
160150
if (existingBeanDefinition != null) {
161-
copyBeanDefinitionDetails(existingBeanDefinition, beanDefinition);
151+
copyBeanDefinitionProperties(existingBeanDefinition, beanDefinition);
162152
registry.removeBeanDefinition(beanName);
163153
}
154+
155+
// At this point, we either removed an existing bean definition above, or
156+
// there was no bean definition to begin with. So, we register the dummy bean
157+
// definition to ensure that a bean definition exists for the given bean name.
164158
registry.registerBeanDefinition(beanName, beanDefinition);
165159

166160
Object override = overrideMetadata.createOverride(beanName, existingBeanDefinition, null);
161+
overrideMetadata.track(override, beanFactory);
162+
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanNameIncludingFactory);
163+
167164
if (beanFactory.isSingleton(beanNameIncludingFactory)) {
168-
// Now we have an instance (the override) that we can register.
169-
// At this stage we don't expect a singleton instance to be present,
170-
// and this call will throw if there is such an instance already.
165+
// Now we have an instance (the override) that we can register. At this
166+
// stage we don't expect a singleton instance to be present, and this call
167+
// will throw an exception if there is such an instance already.
171168
beanFactory.registerSingleton(beanName, override);
172169
}
173-
174-
overrideMetadata.track(override, beanFactory);
175-
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanNameIncludingFactory);
176170
}
177171

178172
private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry,
@@ -231,13 +225,6 @@ private void registerWrapBean(ConfigurableListableBeanFactory beanFactory, Overr
231225
this.overrideRegistrar.registerNameForMetadata(metadata, beanName);
232226
}
233227

234-
RootBeanDefinition createBeanDefinition(OverrideMetadata metadata) {
235-
RootBeanDefinition definition = new RootBeanDefinition(metadata.getBeanType().resolve());
236-
definition.setTargetType(metadata.getBeanType());
237-
definition.setQualifiedElement(metadata.getField());
238-
return definition;
239-
}
240-
241228
private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata metadata,
242229
boolean checkAutowiredCandidate) {
243230

@@ -272,6 +259,26 @@ private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory b
272259
}
273260

274261

262+
private static RootBeanDefinition createBeanDefinition(OverrideMetadata metadata) {
263+
RootBeanDefinition definition = new RootBeanDefinition(metadata.getBeanType().resolve());
264+
definition.setTargetType(metadata.getBeanType());
265+
definition.setQualifiedElement(metadata.getField());
266+
return definition;
267+
}
268+
269+
/**
270+
* Copy the following properties of the source {@link BeanDefinition} to the
271+
* target: the {@linkplain BeanDefinition#isPrimary() primary flag}, the
272+
* {@linkplain BeanDefinition#isFallback() fallback flag}, and the
273+
* {@linkplain BeanDefinition#getScope() scope}.
274+
*/
275+
private static void copyBeanDefinitionProperties(BeanDefinition source, RootBeanDefinition target) {
276+
target.setPrimary(source.isPrimary());
277+
target.setFallback(source.isFallback());
278+
target.setScope(source.getScope());
279+
}
280+
281+
275282
static class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
276283
PriorityOrdered {
277284

spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java

+11-10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
*
5252
* @author Simon Baslé
5353
* @author Stephane Nicoll
54+
* @author Sam Brannen
5455
*/
5556
class BeanOverrideBeanFactoryPostProcessorTests {
5657

@@ -200,7 +201,7 @@ void postProcessorShouldNotTriggerEarlyInitialization() {
200201
}
201202

202203
@Test
203-
void allowReplaceDefinitionWhenSingletonDefinitionPresent() {
204+
void replaceBeanByNameWithMatchingBeanDefinitionWithExplicitSingletonScope() {
204205
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
205206
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
206207
definition.setScope(BeanDefinition.SCOPE_SINGLETON);
@@ -212,7 +213,7 @@ void allowReplaceDefinitionWhenSingletonDefinitionPresent() {
212213
}
213214

214215
@Test
215-
void copyDefinitionPrimaryFallbackAndScope() {
216+
void replaceBeanByNameWithMatchingBeanDefinitionRetainsPrimaryFallbackAndScopeProperties() {
216217
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
217218
context.getBeanFactory().registerScope("customScope", new SimpleThreadScope());
218219
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
@@ -232,22 +233,22 @@ void copyDefinitionPrimaryFallbackAndScope() {
232233
}
233234

234235
@Test
235-
void createDefinitionShouldSetQualifierElement() {
236+
void qualifiedElementIsSetToBeanOverrideField() {
236237
AnnotationConfigApplicationContext context = createContext(CaseByNameWithQualifier.class);
237238
context.registerBeanDefinition("descriptionBean", new RootBeanDefinition(String.class, () -> "ORIGINAL"));
238239

239240
assertThatNoException().isThrownBy(context::refresh);
240241
assertThat(context.getBeanDefinition("descriptionBean"))
241-
.isInstanceOfSatisfying(RootBeanDefinition.class, this::isTheValueField);
242+
.isInstanceOfSatisfying(RootBeanDefinition.class, this::qualifiedElementIsField);
242243
}
243244

244245

245-
private void isTheValueField(RootBeanDefinition def) {
246-
assertThat(def.getQualifiedElement()).isInstanceOfSatisfying(Field.class, field -> {
247-
assertThat(field.getDeclaringClass()).isEqualTo(CaseByNameWithQualifier.class);
248-
assertThat(field.getName()).as("annotated field name")
249-
.isEqualTo("description");
250-
});
246+
private void qualifiedElementIsField(RootBeanDefinition def) {
247+
assertThat(def.getQualifiedElement()).isInstanceOfSatisfying(Field.class,
248+
field -> {
249+
assertThat(field.getDeclaringClass()).isEqualTo(CaseByNameWithQualifier.class);
250+
assertThat(field.getName()).as("annotated field name").isEqualTo("description");
251+
});
251252
}
252253

253254
private AnnotationConfigApplicationContext createContext(Class<?> testClass) {

0 commit comments

Comments
 (0)