From 09eb49207939c530735eacb6ce003735b9391267 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Jul 2015 00:05:39 +0200 Subject: [PATCH] Merged bean definitions are now cached early and selectively evicted after post-processing and before actual bean creation Issue: SPR-12236 --- .../ConfigurableListableBeanFactory.java | 14 ++++++++++- .../factory/support/AbstractBeanFactory.java | 24 ++++++++++++++++++- .../support/DefaultListableBeanFactory.java | 6 +++++ .../PostProcessorRegistrationDelegate.java | 4 ++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java index 0c5ee75fed..3b3f510d58 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,6 +121,18 @@ public interface ConfigurableListableBeanFactory */ Iterator getBeanNamesIterator(); + /** + * Clear the merged bean definition cache, removing entries for beans + * which are not considered eligible for full metadata caching yet. + *

Typically triggered after changes to the original bean definitions, + * e.g. after applying a {@link BeanFactoryPostProcessor}. Note that metadata + * for beans which have already been created at this point will be kept around. + * @since 4.2 + * @see #getBeanDefinition + * @see #getMergedBeanDefinition + */ + void clearMetadataCache(); + /** * Freeze all bean definitions, signalling that the registered bean definitions * will not be modified or post-processed any further. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 22d524d942..66f5d75e03 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -1262,7 +1263,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp // Only cache the merged bean definition if we're already about to create an // instance of the bean, or at least have already created an instance before. - if (containingBd == null && isCacheBeanMetadata() && isBeanEligibleForMetadataCaching(beanName)) { + if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } @@ -1296,6 +1297,23 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp this.mergedBeanDefinitions.remove(beanName); } + /** + * Clear the merged bean definition cache, removing entries for beans + * which are not considered eligible for full metadata caching yet. + *

Typically triggered after changes to the original bean definitions, + * e.g. after applying a {@code BeanFactoryPostProcessor}. Note that metadata + * for beans which have already been created at this point will be kept around. + * @since 4.2 + */ + public void clearMetadataCache() { + Iterator mergedBeans = this.mergedBeanDefinitions.keySet().iterator(); + while (mergedBeans.hasNext()) { + if (!isBeanEligibleForMetadataCaching(mergedBeans.next())) { + mergedBeans.remove(); + } + } + } + /** * Resolve the bean class for the specified bean definition, * resolving a bean class name into a Class reference (if necessary) @@ -1475,6 +1493,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp protected void markBeanAsCreated(String beanName) { if (!this.alreadyCreated.contains(beanName)) { this.alreadyCreated.add(beanName); + + // Let the bean definition get re-merged now that we're actually creating + // the bean... just in case some of its metadata changed in the meantime. + clearMergedBeanDefinition(beanName); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 891a60db34..a79be895e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -708,6 +708,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return iterator; } + @Override + public void clearMetadataCache() { + super.clearMetadataCache(); + clearByTypeCache(); + } + @Override public void freezeConfiguration() { this.configurationFrozen = true; diff --git a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java index 8dfcdf0e05..e7b3b79dee 100644 --- a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java +++ b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java @@ -179,6 +179,10 @@ class PostProcessorRegistrationDelegate { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); + + // Clear cached merged bean definitions since the post-processors might have + // modified the original metadata, e.g. replacing placeholders in values... + beanFactory.clearMetadataCache(); } public static void registerBeanPostProcessors(