diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index b1ce9e61faf1..a30075f27318 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -833,14 +833,18 @@ private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mb } catch (BeansException ex) { // Unexpected target bean mismatch for cached argument -> re-resolve - synchronized (descriptor) { - if (!descriptor.hasShortcut()) { - throw ex; - } + Set autowiredBeanNames = null; + if (descriptor.hasShortcut()) { + // Reset shortcut and try to re-resolve it in this thread... descriptor.setShortcut(null); - Set autowiredBeanNames = new LinkedHashSet<>(2); - argValue = resolveAutowiredArgument(descriptor, paramType, beanName, - autowiredBeanNames, converter, true); + autowiredBeanNames = new LinkedHashSet<>(2); + } + logger.debug("Failed to resolve cached argument", ex); + argValue = resolveAutowiredArgument(descriptor, paramType, beanName, + autowiredBeanNames, converter, true); + if (autowiredBeanNames != null && !descriptor.hasShortcut()) { + // We encountered as stale shortcut before, and the shortcut has + // not been re-resolved by another thread in the meantime... if (argValue != null) { setShortcutIfPossible(descriptor, paramType, autowiredBeanNames); } 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 9170a4abe6f4..b7d89f13de04 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 @@ -446,6 +446,22 @@ public void testOptionalResourceInjectionWithSingletonRemoval() { assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); bf.destroySingleton("testBean"); + bf.registerSingleton("testBeanX", tb); + + bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans()).hasSize(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField).hasSize(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.destroySingleton("testBeanX"); bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class); assertThat(bean.getTestBean()).isNull(); @@ -503,6 +519,22 @@ public void testOptionalResourceInjectionWithBeanDefinitionRemoval() { assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); bf.removeBeanDefinition("testBean"); + bf.registerBeanDefinition("testBeanX", new RootBeanDefinition(TestBean.class)); + + bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class); + assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBeanX")); + assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBeanX")); + assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBeanX")); + assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBeanX")); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans()).hasSize(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField).hasSize(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.removeBeanDefinition("testBeanX"); bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class); assertThat(bean.getTestBean()).isNull(); @@ -791,6 +823,17 @@ public void testConstructorResourceInjectionWithSingletonRemoval() { assertThat(bean.getBeanFactory()).isSameAs(bf); bf.destroySingleton("nestedTestBean"); + bf.registerSingleton("nestedTestBeanX", ntb); + + bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(ntb); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.destroySingleton("nestedTestBeanX"); bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class); assertThat(bean.getTestBean()).isSameAs(tb); @@ -829,6 +872,17 @@ public void testConstructorResourceInjectionWithBeanDefinitionRemoval() { assertThat(bean.getBeanFactory()).isSameAs(bf); bf.removeBeanDefinition("nestedTestBean"); + bf.registerBeanDefinition("nestedTestBeanX", new RootBeanDefinition(NestedTestBean.class)); + + bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBeanX")); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.removeBeanDefinition("nestedTestBeanX"); bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class); assertThat(bean.getTestBean()).isSameAs(tb);