Browse Source

Final merged bean definition clearing in freezeConfiguration

Instead of individual last-minute clearing in markBeanAsCreated, the factory clears all merged bean definitions in freezeConfiguration, retaining the changes of merged bean definition post-processing after that point (in particular in refreshForAotProcessing).

Closes gh-28948
pull/28987/head
Juergen Hoeller 2 years ago
parent
commit
427e5f5899
  1. 8
      spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java
  2. 2
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  3. 4
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
  4. 1
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  5. 11
      spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java
  6. 23
      spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java
  7. 4
      spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java

8
spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -137,7 +137,10 @@ public interface ConfigurableListableBeanFactory
/** /**
* Freeze all bean definitions, signalling that the registered bean definitions * Freeze all bean definitions, signalling that the registered bean definitions
* will not be modified or post-processed any further. * will not be modified or post-processed any further.
* <p>This allows the factory to aggressively cache bean definition metadata. * <p>This allows the factory to aggressively cache bean definition metadata
* going forward, after clearing the initial temporary metadata cache.
* @see #clearMetadataCache()
* @see #isConfigurationFrozen()
*/ */
void freezeConfiguration(); void freezeConfiguration();
@ -145,6 +148,7 @@ public interface ConfigurableListableBeanFactory
* Return whether this factory's bean definitions are frozen, * Return whether this factory's bean definitions are frozen,
* i.e. are not supposed to be modified or post-processed any further. * i.e. are not supposed to be modified or post-processed any further.
* @return {@code true} if the factory's configuration is considered frozen * @return {@code true} if the factory's configuration is considered frozen
* @see #freezeConfiguration()
*/ */
boolean isConfigurationFrozen(); boolean isConfigurationFrozen();

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

@ -576,7 +576,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
throw new BeanCreationException(mbd.getResourceDescription(), beanName, throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex); "Post-processing of merged bean definition failed", ex);
} }
mbd.postProcessed = true; mbd.markAsPostProcessed();
} }
} }

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

@ -1700,12 +1700,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
protected void markBeanAsCreated(String beanName) { protected void markBeanAsCreated(String beanName) {
if (!this.alreadyCreated.contains(beanName)) { if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) { synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) { if (!isBeanEligibleForMetadataCaching(beanName)) {
// Let the bean definition get re-merged now that we're actually creating // 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. // the bean... just in case some of its metadata changed in the meantime.
clearMergedBeanDefinition(beanName); clearMergedBeanDefinition(beanName);
this.alreadyCreated.add(beanName);
} }
this.alreadyCreated.add(beanName);
} }
} }
} }

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

@ -887,6 +887,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override @Override
public void freezeConfiguration() { public void freezeConfiguration() {
clearMetadataCache();
this.configurationFrozen = true; this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames); this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
} }

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

@ -439,6 +439,17 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
} }
} }
/**
* Mark this bean definition as post-processed,
* i.e. processed by {@link MergedBeanDefinitionPostProcessor}.
* @since 6.0
*/
public void markAsPostProcessed() {
synchronized (this.postProcessingLock) {
this.postProcessed = true;
}
}
/** /**
* Register an externally managed configuration method or field. * Register an externally managed configuration method or field.
*/ */

23
spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

@ -772,6 +772,29 @@ class DefaultListableBeanFactoryTests {
assertThat(factory.getType("child")).isEqualTo(DerivedTestBean.class); assertThat(factory.getType("child")).isEqualTo(DerivedTestBean.class);
} }
@Test
void mergedBeanDefinitionChangesRetainedAfterFreezeConfiguration() {
RootBeanDefinition parentDefinition = new RootBeanDefinition(Object.class);
ChildBeanDefinition childDefinition = new ChildBeanDefinition("parent");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("parent", parentDefinition);
factory.registerBeanDefinition("child", childDefinition);
assertThat(factory.getType("parent")).isEqualTo(Object.class);
assertThat(factory.getType("child")).isEqualTo(Object.class);
((RootBeanDefinition) factory.getBeanDefinition("parent")).setBeanClass(TestBean.class);
factory.freezeConfiguration();
assertThat(factory.getType("parent")).isEqualTo(TestBean.class);
assertThat(factory.getType("child")).isEqualTo(TestBean.class);
((RootBeanDefinition) factory.getMergedBeanDefinition("child")).setBeanClass(DerivedTestBean.class);
assertThat(factory.getBean("parent")).isInstanceOf(TestBean.class);
assertThat(factory.getBean("child")).isInstanceOf(DerivedTestBean.class);
}
@Test @Test
void nameAlreadyBound() { void nameAlreadyBound() {
Properties p = new Properties(); Properties p = new Properties();

4
spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java

@ -423,6 +423,7 @@ final class PostProcessorRegistrationDelegate {
} }
} }
private static final class MergedBeanDefinitionPostProcessorInvoker { private static final class MergedBeanDefinitionPostProcessorInvoker {
private final DefaultListableBeanFactory beanFactory; private final DefaultListableBeanFactory beanFactory;
@ -438,12 +439,14 @@ final class PostProcessorRegistrationDelegate {
RootBeanDefinition bd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName); RootBeanDefinition bd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);
Class<?> beanType = resolveBeanType(bd); Class<?> beanType = resolveBeanType(bd);
postProcessRootBeanDefinition(postProcessors, beanName, beanType, bd); postProcessRootBeanDefinition(postProcessors, beanName, beanType, bd);
bd.markAsPostProcessed();
} }
registerBeanPostProcessors(this.beanFactory, postProcessors); registerBeanPostProcessors(this.beanFactory, postProcessors);
} }
private void postProcessRootBeanDefinition(List<MergedBeanDefinitionPostProcessor> postProcessors, private void postProcessRootBeanDefinition(List<MergedBeanDefinitionPostProcessor> postProcessors,
String beanName, Class<?> beanType, RootBeanDefinition bd) { String beanName, Class<?> beanType, RootBeanDefinition bd) {
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, bd); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, bd);
postProcessors.forEach(postProcessor -> postProcessor.postProcessMergedBeanDefinition(bd, beanType, beanName)); postProcessors.forEach(postProcessor -> postProcessor.postProcessMergedBeanDefinition(bd, beanType, beanName));
for (PropertyValue propertyValue : bd.getPropertyValues().getPropertyValueList()) { for (PropertyValue propertyValue : bd.getPropertyValues().getPropertyValueList()) {
@ -466,6 +469,7 @@ final class PostProcessorRegistrationDelegate {
private void resolveInnerBeanDefinition(BeanDefinitionValueResolver valueResolver, BeanDefinition innerBeanDefinition, private void resolveInnerBeanDefinition(BeanDefinitionValueResolver valueResolver, BeanDefinition innerBeanDefinition,
BiConsumer<String, RootBeanDefinition> resolver) { BiConsumer<String, RootBeanDefinition> resolver) {
valueResolver.resolveInnerBean(null, innerBeanDefinition, (name, rbd) -> { valueResolver.resolveInnerBean(null, innerBeanDefinition, (name, rbd) -> {
resolver.accept(name, rbd); resolver.accept(name, rbd);
return Void.class; return Void.class;

Loading…
Cancel
Save