Skip to content

Commit 6dc14af

Browse files
committed
Update view of bean types when an override is detected
Previously, when a bean was overridden and its type changes, BeanTypeRegistry could be left with a stale view of the bean's type. This would lead to incorrect bean condition evaluation as conditions would match or not match based on the bean's old type. This commit updates the type registry to refresh its view of a bean's type when its definition changes. Closes gh-13588
1 parent 57ebdab commit 6dc14af

File tree

2 files changed

+76
-16
lines changed

2 files changed

+76
-16
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
7676

7777
private final Map<String, Class<?>> beanTypes = new HashMap<String, Class<?>>();
7878

79-
private int lastBeanDefinitionCount = 0;
79+
private final Map<String, RootBeanDefinition> beanDefinitions = new HashMap<String, RootBeanDefinition>();
8080

8181
private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
8282
this.beanFactory = beanFactory;
@@ -146,7 +146,7 @@ Set<String> getNamesForAnnotation(Class<? extends Annotation> annotation) {
146146
public void afterSingletonsInstantiated() {
147147
// We're done at this point, free up some memory
148148
this.beanTypes.clear();
149-
this.lastBeanDefinitionCount = 0;
149+
this.beanDefinitions.clear();
150150
}
151151

152152
private void addBeanType(String name) {
@@ -159,10 +159,23 @@ else if (!this.beanFactory.isAlias(name)) {
159159
}
160160

161161
private void addBeanTypeForNonAliasDefinition(String name) {
162+
addBeanTypeForNonAliasDefinition(name, getBeanDefinition(name));
163+
}
164+
165+
private RootBeanDefinition getBeanDefinition(String name) {
166+
try {
167+
return (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(name);
168+
}
169+
catch (BeanDefinitionStoreException ex) {
170+
logIgnoredError("unresolvable metadata in bean definition", name, ex);
171+
return null;
172+
}
173+
}
174+
175+
private void addBeanTypeForNonAliasDefinition(String name,
176+
RootBeanDefinition beanDefinition) {
162177
try {
163178
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
164-
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
165-
.getMergedBeanDefinition(name);
166179
if (!beanDefinition.isAbstract()
167180
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
168181
if (this.beanFactory.isFactoryBean(factoryName)) {
@@ -176,15 +189,12 @@ private void addBeanTypeForNonAliasDefinition(String name) {
176189
this.beanTypes.put(name, this.beanFactory.getType(name));
177190
}
178191
}
192+
this.beanDefinitions.put(name, beanDefinition);
179193
}
180194
catch (CannotLoadBeanClassException ex) {
181195
// Probably contains a placeholder
182196
logIgnoredError("bean class loading failure for bean", name, ex);
183197
}
184-
catch (BeanDefinitionStoreException ex) {
185-
// Probably contains a placeholder
186-
logIgnoredError("unresolvable metadata in bean definition", name, ex);
187-
}
188198
}
189199

190200
private void logIgnoredError(String message, String name, Exception ex) {
@@ -199,15 +209,24 @@ private boolean requiresEagerInit(String factoryBeanName) {
199209
}
200210

201211
private void updateTypesIfNecessary() {
202-
if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
203-
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
204-
while (names.hasNext()) {
205-
String name = names.next();
206-
if (!this.beanTypes.containsKey(name)) {
207-
addBeanType(name);
212+
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
213+
while (names.hasNext()) {
214+
String name = names.next();
215+
if (!this.beanTypes.containsKey(name)) {
216+
addBeanType(name);
217+
}
218+
else {
219+
if (!this.beanFactory.isAlias(name)
220+
&& !this.beanFactory.containsSingleton(name)) {
221+
RootBeanDefinition beanDefinition = getBeanDefinition(name);
222+
RootBeanDefinition existingDefinition = this.beanDefinitions.put(name,
223+
beanDefinition);
224+
if (existingDefinition != null
225+
&& !beanDefinition.equals(existingDefinition)) {
226+
addBeanTypeForNonAliasDefinition(name, beanDefinition);
227+
}
208228
}
209229
}
210-
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
211230
}
212231
}
213232

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -139,6 +139,16 @@ public void beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation() {
139139
assertThat(this.context.getBeansOfType(ExampleBean.class)).hasSize(1);
140140
}
141141

142+
@Test
143+
public void conditionEvaluationConsidersChangeInTypeWhenBeanIsOverridden() {
144+
this.context.register(OriginalDefinition.class, OverridingDefinition.class,
145+
ConsumingConfiguration.class);
146+
this.context.refresh();
147+
assertThat(this.context.containsBean("testBean")).isTrue();
148+
assertThat(this.context.getBean(Integer.class)).isEqualTo(1);
149+
assertThat(this.context.getBeansOfType(ConsumingConfiguration.class)).isEmpty();
150+
}
151+
142152
@Configuration
143153
@ConditionalOnBean(name = "foo")
144154
protected static class OnBeanNameConfiguration {
@@ -311,4 +321,35 @@ public String toString() {
311321

312322
}
313323

324+
@Configuration
325+
public static class OriginalDefinition {
326+
327+
@Bean
328+
public String testBean() {
329+
return "test";
330+
}
331+
332+
}
333+
334+
@Configuration
335+
@ConditionalOnBean(String.class)
336+
public static class OverridingDefinition {
337+
338+
@Bean
339+
public Integer testBean() {
340+
return 1;
341+
}
342+
343+
}
344+
345+
@Configuration
346+
@ConditionalOnBean(String.class)
347+
public static class ConsumingConfiguration {
348+
349+
ConsumingConfiguration(String testBean) {
350+
351+
}
352+
353+
}
354+
314355
}

0 commit comments

Comments
 (0)