diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/SmartObjectFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/SmartObjectFactory.java index 9ae7523a03350c22d2731dd27fcb0b1803554698..e4dc517451565e7072d320bb30b5ed11d19038d2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/SmartObjectFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/SmartObjectFactory.java @@ -28,8 +28,20 @@ import org.springframework.beans.BeansException; public interface SmartObjectFactory extends ObjectFactory { /** - * Return an instance (possibly shared or independent) - * of the object managed by this factory. + * Return an instance (possibly shared or independent) of the object + * managed by this factory. + *

Allows for specifying explicit construction arguments, along the + * lines of {@link BeanFactory#getBean(String, Object...)}. + * @param args arguments to use when creating a corresponding instance + * @return an instance of the bean + * @throws BeansException in case of creation errors + * @see #getObject() + */ + T getObject(Object... args) throws BeansException; + + /** + * Return an instance (possibly shared or independent) of the object + * managed by this factory. * @return an instance of the bean, or {@code null} if not available * @throws BeansException in case of creation errors * @see #getObject() @@ -37,8 +49,8 @@ public interface SmartObjectFactory extends ObjectFactory { T getIfAvailable() throws BeansException; /** - * Return an instance (possibly shared or independent) - * of the object managed by this factory. + * Return an instance (possibly shared or independent) of the object + * managed by this factory. * @return an instance of the bean, or {@code null} if not available or * not unique (i.e. multiple candidates found with none marked as primary) * @throws BeansException in case of creation errors 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 f109ca8efb52e02a3c1436454cdd5d4267512b5b..cadc893a1e8f5198279cc7e3c497b7326d7d299b 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 @@ -26,6 +26,7 @@ import java.lang.reflect.Type; import java.util.Map; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericTypeResolver; @@ -200,6 +201,20 @@ public class DependencyDescriptor implements Serializable { throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } + /** + * Resolve the specified bean name, as a candidate result of the matching + * algorithm for this dependency, to a bean instance from the given factory. + *

The default implementation calls {@link BeanFactory#getBean(String)}. + * Subclasses may provide additional arguments or other customizations. + * @param beanName the bean name, as a candidate result for this dependency + * @param beanFactory the associated factory + * @return the bean instance (never {@code null}) + * @see BeanFactory#getBean(String) + */ + public Object resolveCandidate(String beanName, BeanFactory beanFactory) { + return beanFactory.getBean(beanName); + } + /** * 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 a6d25e553f94bfb17887a64c8be7a3fec0bc44d9..647a8c069d04c329fb7295102bdcda4117704750 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 @@ -1052,7 +1052,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (matchingBeans.size() > 1) { String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { - if (!indicatesMultipleBeans(type) || descriptor.isRequired()) { + if (descriptor.isRequired() || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { @@ -1198,7 +1198,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { - result.put(candidateName, getBean(candidateName)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); } } if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { @@ -1206,14 +1206,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, getBean(candidateName)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); } } if (result.isEmpty()) { // Consider self references before as a final pass for (String candidateName : candidateNames) { if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, getBean(candidateName)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); } } } @@ -1462,12 +1462,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @UsesJava8 private class OptionalDependencyFactory { - public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) { + public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) { DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { @Override public boolean isRequired() { return false; } + @Override + public Object resolveCandidate(String beanName, BeanFactory beanFactory) { + return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) : + super.resolveCandidate(beanName, beanFactory)); + } }; descriptorToUse.increaseNestingLevel(); return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null)); @@ -1503,6 +1508,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } + @Override + public Object getObject(final Object... args) throws BeansException { + if (this.optional) { + return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName, args); + } + else { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + @Override + public Object resolveCandidate(String beanName, BeanFactory beanFactory) { + return beanFactory.getBean(beanName, args); + } + }; + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } + } + @Override public Object getIfAvailable() throws BeansException { if (this.optional) { 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 9ce9eabb81907c0969d0ca433f8c9266c873d2e9..48bb4f5cd97ed6b3d73f75a287919ccc44799004 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 @@ -1047,7 +1047,26 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test - public void testSmartObjectFactoryInjection() { + public void testSmartObjectFactoryInjectionWithPrototype() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class); + tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("testBean", tbd); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + assertEquals(bf.getBean("testBean"), bean.getTestBean()); + assertEquals(bf.getBean("testBean", "myName"), bean.getTestBean("myName")); + assertEquals(bf.getBean("testBean"), bean.getOptionalTestBean()); + assertEquals(bf.getBean("testBean"), bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test + public void testSmartObjectFactoryInjectionWithSingletonTarget() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(bf); @@ -2557,6 +2576,10 @@ public class AutowiredAnnotationBeanPostProcessorTests { return this.testBeanFactory.getObject(); } + public TestBean getTestBean(String name) { + return this.testBeanFactory.getObject(name); + } + public TestBean getOptionalTestBean() { return this.testBeanFactory.getIfAvailable(); }