Refresh cached value after unexpected mismatch (e.g. null vs non-null)
In addition to the previously addressed removal of bean definitions, this is able to deal with prototype factory methods returning non-null after null or also null after non-null. Stale cached values are getting refreshed rather than bypassed.
Closes gh-30794
(cherry picked from commit 0226580773)
This commit is contained in:
@@ -594,7 +594,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* Resolve the specified cached method argument or field value.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
@@ -629,10 +629,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
try {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
value = resolveCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -661,9 +663,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
cachedFieldValue = desc;
|
||||
Object cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (value != null && autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
@@ -673,9 +674,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
@@ -709,10 +714,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
try {
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -731,14 +738,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName) {
|
||||
Object[] cachedMethodArguments = this.cachedMethodArguments;
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
|
||||
if (cachedMethodArguments == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] arguments = new Object[cachedMethodArguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
@@ -771,7 +777,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||
registerDependentBeans(beanName, autowiredBeans);
|
||||
if (autowiredBeans.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeans.iterator();
|
||||
@@ -786,11 +792,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
|
||||
@@ -154,6 +154,59 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceInjectionWithSometimesNullBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtendedResourceInjection() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
|
||||
@@ -3902,6 +3955,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class SometimesNullFactoryMethods {
|
||||
|
||||
public static boolean active = false;
|
||||
|
||||
public static TestBean createTestBean() {
|
||||
return (active ? new TestBean() : null);
|
||||
}
|
||||
|
||||
public static NestedTestBean createNestedTestBean() {
|
||||
return (active ? new NestedTestBean() : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ProvidedArgumentBean {
|
||||
|
||||
public ProvidedArgumentBean(String[] args) {
|
||||
|
||||
Reference in New Issue
Block a user