Browse Source

Cache resolved singleton beans in injected Provider instance

Includes alignment for direct Optional injection points, consistently registering an autowiredBeanNames entry for an Optional as well as a non-Optional injection result.

Closes gh-35373
Closes gh-35919
pull/35933/head
Juergen Hoeller 2 months ago
parent
commit
4c4161c8c0
  1. 67
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  2. 32
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
  3. 47
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java

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

@ -1632,7 +1632,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1632,7 +1632,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
return createOptionalDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
@ -2330,8 +2330,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2330,8 +2330,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
/**
* Create an {@link Optional} wrapper for the specified dependency.
*/
private Optional<?> createOptionalDependency(
DependencyDescriptor descriptor, @Nullable String beanName, final @Nullable Object... args) {
private Optional<?> createOptionalDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable Object @Nullable [] args) {
DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
@Override
@ -2348,7 +2348,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2348,7 +2348,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return ObjectUtils.isEmpty(args);
}
};
Object result = doResolveDependency(descriptorToUse, beanName, null, null);
Object result = doResolveDependency(descriptorToUse, beanName, autowiredBeanNames, null);
return (result instanceof Optional<?> optional ? optional : Optional.ofNullable(result));
}
@ -2501,12 +2501,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2501,12 +2501,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
*/
private class DependencyObjectProvider implements BeanObjectProvider<Object> {
private static final Object NOT_CACHEABLE = new Object();
private static final Object NULL_VALUE = new Object();
private final DependencyDescriptor descriptor;
private final boolean optional;
private final @Nullable String beanName;
private transient volatile @Nullable Object cachedValue;
public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {
this.descriptor = new NestedDependencyDescriptor(descriptor);
this.optional = (this.descriptor.getDependencyType() == Optional.class);
@ -2515,22 +2521,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2515,22 +2521,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public Object getObject() throws BeansException {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName);
}
else {
Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
if (result == null) {
throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
}
return result;
Object result = getValue();
if (result == null) {
throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
}
return result;
}
@Override
public Object getObject(final @Nullable Object... args) throws BeansException {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName, args);
return createOptionalDependency(this.descriptor, this.beanName, null, args);
}
else {
DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
@ -2551,7 +2552,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2551,7 +2552,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
public @Nullable Object getIfAvailable() throws BeansException {
try {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName);
return createOptionalDependency(this.descriptor, this.beanName, null, null);
}
else {
DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
@ -2604,7 +2605,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2604,7 +2605,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
};
try {
if (this.optional) {
return createOptionalDependency(descriptorToUse, this.beanName);
return createOptionalDependency(descriptorToUse, this.beanName, null, null);
}
else {
return doResolveDependency(descriptorToUse, this.beanName, null, null);
@ -2630,11 +2631,41 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -2630,11 +2631,41 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
protected @Nullable Object getValue() throws BeansException {
Object value = this.cachedValue;
if (value == null) {
if (isConfigurationFrozen()) {
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
value = resolveValue(autowiredBeanNames);
boolean cacheable = false;
if (!autowiredBeanNames.isEmpty()) {
cacheable = true;
for (String autowiredBeanName : autowiredBeanNames) {
if (!containsBean(autowiredBeanName) || !isSingleton(autowiredBeanName)) {
cacheable = false;
}
}
}
this.cachedValue = (cacheable ? (value != null ? value : NULL_VALUE) : NOT_CACHEABLE);
return value;
}
}
else if (value == NULL_VALUE) {
return null;
}
else if (value != NOT_CACHEABLE) {
return value;
}
// Not cacheable -> fresh resolution.
return resolveValue(null);
}
private @Nullable Object resolveValue(@Nullable Set<String> autowiredBeanNames) {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName);
return createOptionalDependency(this.descriptor, this.beanName, autowiredBeanNames, null);
}
else {
return doResolveDependency(this.descriptor, this.beanName, null, null);
return doResolveDependency(this.descriptor, this.beanName, autowiredBeanNames, null);
}
}

32
spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

@ -1587,6 +1587,18 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -1587,6 +1587,18 @@ class AutowiredAnnotationBeanPostProcessorTests {
ObjectFactoryFieldInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
void objectFactoryFieldInjectionAgainstFrozen() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryFieldInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.freezeConfiguration();
ObjectFactoryFieldInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
@ -1596,6 +1608,18 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -1596,6 +1608,18 @@ class AutowiredAnnotationBeanPostProcessorTests {
ObjectFactoryConstructorInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryConstructorInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
void objectFactoryConstructorInjectionAgainstFrozen() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryConstructorInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.freezeConfiguration();
ObjectFactoryConstructorInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryConstructorInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
@ -2116,8 +2140,8 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2116,8 +2140,8 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("factoryBeanDependentBean", new RootBeanDefinition(FactoryBeanDependentBean.class));
bf.registerSingleton("stringFactoryBean", new StringFactoryBean());
final StringFactoryBean factoryBean = (StringFactoryBean) bf.getBean("&stringFactoryBean");
final FactoryBeanDependentBean bean = (FactoryBeanDependentBean) bf.getBean("factoryBeanDependentBean");
StringFactoryBean factoryBean = (StringFactoryBean) bf.getBean("&stringFactoryBean");
FactoryBeanDependentBean bean = (FactoryBeanDependentBean) bf.getBean("factoryBeanDependentBean");
assertThat(factoryBean).as("The singleton StringFactoryBean should have been registered.").isNotNull();
assertThat(bean).as("The factoryBeanDependentBean should have been registered.").isNotNull();
@ -2728,9 +2752,11 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2728,9 +2752,11 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nonNullBean", "Test");
bf.registerBeanDefinition("mixedNullableInjectionBean",
new RootBeanDefinition(MixedNullableInjectionBean.class));
MixedNullableInjectionBean mixedNullableInjectionBean = bf.getBean(MixedNullableInjectionBean.class);
assertThat(mixedNullableInjectionBean.nonNullBean).isNotNull();
assertThat(mixedNullableInjectionBean.nullableBean).isNull();
assertThat(bf.getDependentBeans("nonNullBean")).contains("mixedNullableInjectionBean");
}
@Test
@ -2738,9 +2764,11 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2738,9 +2764,11 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nonNullBean", "Test");
bf.registerBeanDefinition("mixedOptionalInjectionBean",
new RootBeanDefinition(MixedOptionalInjectionBean.class));
MixedOptionalInjectionBean mixedOptionalInjectionBean = bf.getBean(MixedOptionalInjectionBean.class);
assertThat(mixedOptionalInjectionBean.nonNullBean).isNotNull();
assertThat(mixedOptionalInjectionBean.nullableBean).isNull();
assertThat(bf.getDependentBeans("nonNullBean")).contains("mixedOptionalInjectionBean");
}

47
spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java

@ -370,8 +370,25 @@ class InjectAnnotationBeanPostProcessorTests { @@ -370,8 +370,25 @@ class InjectAnnotationBeanPostProcessorTests {
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
bean = SerializationTestUtils.serializeAndDeserialize(bean);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
void testObjectFactoryWithBeanFieldAgainstFrozen() throws Exception {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryFieldInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.setSerializationId("test");
bf.freezeConfiguration();
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
bean = SerializationTestUtils.serializeAndDeserialize(bean);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
@ -382,8 +399,25 @@ class InjectAnnotationBeanPostProcessorTests { @@ -382,8 +399,25 @@ class InjectAnnotationBeanPostProcessorTests {
ObjectFactoryMethodInjectionBean bean = (ObjectFactoryMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
bean = SerializationTestUtils.serializeAndDeserialize(bean);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
void testObjectFactoryWithBeanMethodAgainstFrozen() throws Exception {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryMethodInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.setSerializationId("test");
bf.freezeConfiguration();
ObjectFactoryMethodInjectionBean bean = (ObjectFactoryMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
bean = SerializationTestUtils.serializeAndDeserialize(bean);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@Test
@ -444,8 +478,8 @@ class InjectAnnotationBeanPostProcessorTests { @@ -444,8 +478,8 @@ class InjectAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("factoryBeanDependentBean", new RootBeanDefinition(FactoryBeanDependentBean.class));
bf.registerSingleton("stringFactoryBean", new StringFactoryBean());
final StringFactoryBean factoryBean = (StringFactoryBean) bf.getBean("&stringFactoryBean");
final FactoryBeanDependentBean bean = (FactoryBeanDependentBean) bf.getBean("factoryBeanDependentBean");
StringFactoryBean factoryBean = (StringFactoryBean) bf.getBean("&stringFactoryBean");
FactoryBeanDependentBean bean = (FactoryBeanDependentBean) bf.getBean("factoryBeanDependentBean");
assertThat(factoryBean).as("The singleton StringFactoryBean should have been registered.").isNotNull();
assertThat(bean).as("The factoryBeanDependentBean should have been registered.").isNotNull();
@ -459,6 +493,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -459,6 +493,7 @@ class InjectAnnotationBeanPostProcessorTests {
NullableFieldInjectionBean bean = (NullableFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -476,6 +511,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -476,6 +511,7 @@ class InjectAnnotationBeanPostProcessorTests {
NullableMethodInjectionBean bean = (NullableMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -494,6 +530,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -494,6 +530,7 @@ class InjectAnnotationBeanPostProcessorTests {
OptionalFieldInjectionBean bean = (OptionalFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -512,6 +549,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -512,6 +549,7 @@ class InjectAnnotationBeanPostProcessorTests {
OptionalMethodInjectionBean bean = (OptionalMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -530,6 +568,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -530,6 +568,7 @@ class InjectAnnotationBeanPostProcessorTests {
OptionalListFieldInjectionBean bean = (OptionalListFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).hasValueSatisfying(list ->
assertThat(list).containsExactly(bf.getBean("testBean", TestBean.class)));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -548,6 +587,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -548,6 +587,7 @@ class InjectAnnotationBeanPostProcessorTests {
OptionalListMethodInjectionBean bean = (OptionalListMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).hasValueSatisfying(list ->
assertThat(list).containsExactly(bf.getBean("testBean", TestBean.class)));
assertThat(bf.getDependentBeans("testBean")).contains("annotatedBean");
}
@Test
@ -566,6 +606,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -566,6 +606,7 @@ class InjectAnnotationBeanPostProcessorTests {
ProviderOfOptionalFieldInjectionBean bean = (ProviderOfOptionalFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).doesNotContain("annotatedBean");
}
@Test
@ -584,6 +625,7 @@ class InjectAnnotationBeanPostProcessorTests { @@ -584,6 +625,7 @@ class InjectAnnotationBeanPostProcessorTests {
ProviderOfOptionalMethodInjectionBean bean = (ProviderOfOptionalMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get()).isSameAs(bf.getBean("testBean"));
assertThat(bf.getDependentBeans("testBean")).doesNotContain("annotatedBean");
}
@Test
@ -788,7 +830,6 @@ class InjectAnnotationBeanPostProcessorTests { @@ -788,7 +830,6 @@ class InjectAnnotationBeanPostProcessorTests {
private ConfigurableListableBeanFactory beanFactory;
public ConstructorResourceInjectionBean() {
throw new UnsupportedOperationException();
}

Loading…
Cancel
Save