Skip to content

Commit 95edcb8

Browse files
philwebbjhoeller
authored andcommitted
Retain merged bean definition caches when possible
Update the logic in `AbstractBeanFactory` so that caches from merged bean definitions remain whenever possible. Prior to this commit, all merged bean definitions would be completely removed after bean post processing in case a processor changed the bean type. It's fairly unlikely these days that the bean type will actually change, so instead we now compare a subset of the old cached properties against the newly created definition. Only if key properties have changed do we now discard the older cached values. Closes gh-23336
1 parent 2ee1ce6 commit 95edcb8

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
* @author Costin Leau
115115
* @author Chris Beams
116116
* @author Sam Brannen
117+
* @author Phillip Webb
117118
* @since 13.02.2004
118119
* @see RootBeanDefinition
119120
* @see DefaultListableBeanFactory
@@ -646,16 +647,16 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
646647
@Nullable
647648
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
648649
Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch);
649-
650650
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
651651
// eventual type after a before-instantiation shortcut.
652652
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
653+
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
653654
for (BeanPostProcessor bp : getBeanPostProcessors()) {
654655
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
655656
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
656657
Class<?> predicted = ibp.predictBeanType(targetType, beanName);
657-
if (predicted != null && (typesToMatch.length != 1 || FactoryBean.class != typesToMatch[0] ||
658-
FactoryBean.class.isAssignableFrom(predicted))) {
658+
if (predicted != null &&
659+
(!matchingOnlyFactoryBean || FactoryBean.class.isAssignableFrom(predicted))) {
659660
return predicted;
660661
}
661662
}

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java

+31-5
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
* @author Juergen Hoeller
104104
* @author Costin Leau
105105
* @author Chris Beams
106+
* @author Phillip Webb
106107
* @since 15 April 2001
107108
* @see #getBeanDefinition
108109
* @see #createBean
@@ -1215,7 +1216,7 @@ protected void registerCustomEditors(PropertyEditorRegistry registry) {
12151216
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
12161217
// Quick check on the concurrent map first, with minimal locking.
12171218
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
1218-
if (mbd != null) {
1219+
if (mbd != null && !mbd.stale) {
12191220
return mbd;
12201221
}
12211222
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
@@ -1251,13 +1252,16 @@ protected RootBeanDefinition getMergedBeanDefinition(
12511252

12521253
synchronized (this.mergedBeanDefinitions) {
12531254
RootBeanDefinition mbd = null;
1255+
RootBeanDefinition previous = null;
12541256

12551257
// Check with full lock now in order to enforce the same merged instance.
12561258
if (containingBd == null) {
12571259
mbd = this.mergedBeanDefinitions.get(beanName);
12581260
}
12591261

1260-
if (mbd == null) {
1262+
if (mbd == null || mbd.stale) {
1263+
previous = mbd;
1264+
mbd = null;
12611265
if (bd.getParentName() == null) {
12621266
// Use copy of given root bean definition.
12631267
if (bd instanceof RootBeanDefinition) {
@@ -1315,11 +1319,26 @@ protected RootBeanDefinition getMergedBeanDefinition(
13151319
this.mergedBeanDefinitions.put(beanName, mbd);
13161320
}
13171321
}
1318-
1322+
if (previous != null) {
1323+
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
1324+
}
13191325
return mbd;
13201326
}
13211327
}
13221328

1329+
private void copyRelevantMergedBeanDefinitionCaches(RootBeanDefinition previous,
1330+
RootBeanDefinition mbd) {
1331+
if (ObjectUtils.nullSafeEquals(mbd.getBeanClassName(), previous.getBeanClassName()) &&
1332+
ObjectUtils.nullSafeEquals(mbd.getFactoryBeanName(), previous.getFactoryBeanName()) &&
1333+
ObjectUtils.nullSafeEquals(mbd.getFactoryMethodName(), previous.getFactoryMethodName()) &&
1334+
(mbd.targetType == null || mbd.targetType.equals(previous.targetType))) {
1335+
mbd.targetType = previous.targetType;
1336+
mbd.resolvedTargetType = previous.resolvedTargetType;
1337+
mbd.factoryMethodReturnType = previous.factoryMethodReturnType;
1338+
mbd.factoryMethodToIntrospect = previous.factoryMethodToIntrospect;
1339+
}
1340+
}
1341+
13231342
/**
13241343
* Check the given merged bean definition,
13251344
* potentially throwing validation exceptions.
@@ -1342,7 +1361,10 @@ protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName
13421361
* @param beanName the bean name to clear the merged definition for
13431362
*/
13441363
protected void clearMergedBeanDefinition(String beanName) {
1345-
this.mergedBeanDefinitions.remove(beanName);
1364+
RootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
1365+
if (bd != null) {
1366+
bd.stale = true;
1367+
}
13461368
}
13471369

13481370
/**
@@ -1354,7 +1376,11 @@ protected void clearMergedBeanDefinition(String beanName) {
13541376
* @since 4.2
13551377
*/
13561378
public void clearMetadataCache() {
1357-
this.mergedBeanDefinitions.keySet().removeIf(bean -> !isBeanEligibleForMetadataCaching(bean));
1379+
this.mergedBeanDefinitions.forEach((beanName, bd) -> {
1380+
if (!isBeanEligibleForMetadataCaching(beanName)) {
1381+
bd.stale = true;
1382+
}
1383+
});
13581384
}
13591385

13601386
/**

spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
6060
@Nullable
6161
private AnnotatedElement qualifiedElement;
6262

63+
/** Determines if the definition needs to be re-merged. */
64+
volatile boolean stale;
65+
6366
boolean allowCaching = true;
6467

6568
boolean isFactoryMethodUnique = false;

0 commit comments

Comments
 (0)