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 10a0dc76fc0..3848510f98f 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 @@ -51,7 +51,6 @@ import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; -import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -529,9 +528,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument; return this.beanFactory.resolveDependency(descriptor, beanName, null, null); } - else if (cachedArgument instanceof RuntimeBeanReference) { - return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName()); - } else { return cachedArgument; } @@ -581,7 +577,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { - this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); + this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName); } } } @@ -665,7 +661,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { - this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName); + this.cachedMethodArguments[i] = + new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName); } } } @@ -701,4 +698,24 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean } } + + /** + * DependencyDescriptor variant with a pre-resolved target bean name. + */ + @SuppressWarnings("serial") + private static class ShortcutDependencyDescriptor extends DependencyDescriptor { + + private final String shortcutBeanName; + + public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutBeanName) { + super(original); + this.shortcutBeanName = shortcutBeanName; + } + + @Override + public Object resolveShortcut(BeanFactory beanFactory) { + return resolveCandidate(this.shortcutBeanName, beanFactory); + } + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index c38deb74800..3533840635a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -187,6 +187,21 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable return beanFactory.getBean(beanName); } + /** + * Resolve a shortcut for this dependency against the given factory, for example + * taking some pre-resolved information into account. + *

The resolution algorithm will first attempt to resolve a shortcut through this + * method before going into the regular type matching algorithm across all beans. + * Subclasses may override this method to improve resolution performance based on + * pre-cached information while still receiving {@link InjectionPoint} exposure etc. + * @param beanFactory the associated factory + * @return the shortcut result if any, or {@code null} if none + * @since 4.3.1 + */ + public Object resolveShortcut(BeanFactory beanFactory) { + return null; + } + /** * Increase this descriptor's nesting level. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 4516cc5f01c..6205fa69cfe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1024,27 +1024,32 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { - Class type = descriptor.getDependencyType(); - Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); - if (value != null) { - if (value instanceof String) { - String strVal = resolveEmbeddedValue((String) value); - BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); - value = evaluateBeanDefinitionString(strVal, bd); - } - TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); - return (descriptor.getField() != null ? - converter.convertIfNecessary(value, type, descriptor.getField()) : - converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); - } - - Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); - if (multipleBeans != null) { - return multipleBeans; - } - InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { + Object shortcut = descriptor.resolveShortcut(this); + if (shortcut != null) { + return shortcut; + } + + Class type = descriptor.getDependencyType(); + Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); + if (value != null) { + if (value instanceof String) { + String strVal = resolveEmbeddedValue((String) value); + BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); + value = evaluateBeanDefinitionString(strVal, bd); + } + TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); + return (descriptor.getField() != null ? + converter.convertIfNecessary(value, type, descriptor.getField()) : + converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); + } + + Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); + if (multipleBeans != null) { + return multipleBeans; + } + Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java index 498453338e7..bf4c9e6a9eb 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java @@ -31,9 +31,7 @@ import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver; import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.Value; @@ -210,16 +208,17 @@ public class ConfigurationClassProcessingTests { @Test public void configurationWithAdaptivePrototypes() { - DefaultListableBeanFactory factory = - initBeanFactory(ConfigWithPrototypeBean.class, AdaptiveInjectionPoints.class); - AutowiredAnnotationBeanPostProcessor aabpp = new AutowiredAnnotationBeanPostProcessor(); - aabpp.setBeanFactory(factory); - factory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); - factory.addBeanPostProcessor(aabpp); + AnnotationConfigApplicationContext factory = new AnnotationConfigApplicationContext(); + factory.register(ConfigWithPrototypeBean.class, AdaptiveInjectionPoints.class); + factory.refresh(); AdaptiveInjectionPoints adaptive = factory.getBean(AdaptiveInjectionPoints.class); assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName()); - assertEquals("adaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); + assertEquals("setAdaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); + + adaptive = factory.getBean(AdaptiveInjectionPoints.class); + assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName()); + assertEquals("setAdaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); } @Test @@ -357,13 +356,18 @@ public class ConfigurationClassProcessingTests { } + @Scope("prototype") static class AdaptiveInjectionPoints { @Autowired @Qualifier("adaptive1") public TestBean adaptiveInjectionPoint1; - @Autowired @Qualifier("adaptive2") public TestBean adaptiveInjectionPoint2; + + @Autowired @Qualifier("adaptive2") + public void setAdaptiveInjectionPoint2(TestBean adaptiveInjectionPoint2) { + this.adaptiveInjectionPoint2 = adaptiveInjectionPoint2; + } }