mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-01 11:49:04 +00:00
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
This commit is contained in:
+49
-18
@@ -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
|
||||
/**
|
||||
* 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
|
||||
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
|
||||
*/
|
||||
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
|
||||
|
||||
@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
|
||||
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
|
||||
};
|
||||
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
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-2
@@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+44
-3
@@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public ConstructorResourceInjectionBean() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user