diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 74caf783f3..d77880c012 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -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 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; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index f88b9a6387..f3ec59432d 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -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) {