Skip to content

Commit 890819f

Browse files
committed
SmartObjectFactory provides getObject(args) variant as well
Issue: SPR-13956
1 parent 5ed9046 commit 890819f

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/SmartObjectFactory.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,29 @@
2828
public interface SmartObjectFactory<T> extends ObjectFactory<T> {
2929

3030
/**
31-
* Return an instance (possibly shared or independent)
32-
* of the object managed by this factory.
31+
* Return an instance (possibly shared or independent) of the object
32+
* managed by this factory.
33+
* <p>Allows for specifying explicit construction arguments, along the
34+
* lines of {@link BeanFactory#getBean(String, Object...)}.
35+
* @param args arguments to use when creating a corresponding instance
36+
* @return an instance of the bean
37+
* @throws BeansException in case of creation errors
38+
* @see #getObject()
39+
*/
40+
T getObject(Object... args) throws BeansException;
41+
42+
/**
43+
* Return an instance (possibly shared or independent) of the object
44+
* managed by this factory.
3345
* @return an instance of the bean, or {@code null} if not available
3446
* @throws BeansException in case of creation errors
3547
* @see #getObject()
3648
*/
3749
T getIfAvailable() throws BeansException;
3850

3951
/**
40-
* Return an instance (possibly shared or independent)
41-
* of the object managed by this factory.
52+
* Return an instance (possibly shared or independent) of the object
53+
* managed by this factory.
4254
* @return an instance of the bean, or {@code null} if not available or
4355
* not unique (i.e. multiple candidates found with none marked as primary)
4456
* @throws BeansException in case of creation errors

spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727

2828
import org.springframework.beans.BeansException;
29+
import org.springframework.beans.factory.BeanFactory;
2930
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
3031
import org.springframework.core.GenericCollectionTypeResolver;
3132
import org.springframework.core.GenericTypeResolver;
@@ -200,6 +201,20 @@ public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans)
200201
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
201202
}
202203

204+
/**
205+
* Resolve the specified bean name, as a candidate result of the matching
206+
* algorithm for this dependency, to a bean instance from the given factory.
207+
* <p>The default implementation calls {@link BeanFactory#getBean(String)}.
208+
* Subclasses may provide additional arguments or other customizations.
209+
* @param beanName the bean name, as a candidate result for this dependency
210+
* @param beanFactory the associated factory
211+
* @return the bean instance (never {@code null})
212+
* @see BeanFactory#getBean(String)
213+
*/
214+
public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
215+
return beanFactory.getBean(beanName);
216+
}
217+
203218

204219
/**
205220
* Increase this descriptor's nesting level.

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
10521052
if (matchingBeans.size() > 1) {
10531053
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
10541054
if (primaryBeanName == null) {
1055-
if (!indicatesMultipleBeans(type) || descriptor.isRequired()) {
1055+
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
10561056
return descriptor.resolveNotUnique(type, matchingBeans);
10571057
}
10581058
else {
@@ -1198,22 +1198,22 @@ protected Map<String, Object> findAutowireCandidates(
11981198
}
11991199
for (String candidateName : candidateNames) {
12001200
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
1201-
result.put(candidateName, getBean(candidateName));
1201+
result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
12021202
}
12031203
}
12041204
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
12051205
// Consider fallback matches if the first pass failed to find anything...
12061206
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
12071207
for (String candidateName : candidateNames) {
12081208
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
1209-
result.put(candidateName, getBean(candidateName));
1209+
result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
12101210
}
12111211
}
12121212
if (result.isEmpty()) {
12131213
// Consider self references before as a final pass
12141214
for (String candidateName : candidateNames) {
12151215
if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
1216-
result.put(candidateName, getBean(candidateName));
1216+
result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
12171217
}
12181218
}
12191219
}
@@ -1462,12 +1462,17 @@ private Object readResolve() {
14621462
@UsesJava8
14631463
private class OptionalDependencyFactory {
14641464

1465-
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) {
1465+
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) {
14661466
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
14671467
@Override
14681468
public boolean isRequired() {
14691469
return false;
14701470
}
1471+
@Override
1472+
public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
1473+
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
1474+
super.resolveCandidate(beanName, beanFactory));
1475+
}
14711476
};
14721477
descriptorToUse.increaseNestingLevel();
14731478
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
@@ -1503,6 +1508,22 @@ public Object getObject() throws BeansException {
15031508
}
15041509
}
15051510

1511+
@Override
1512+
public Object getObject(final Object... args) throws BeansException {
1513+
if (this.optional) {
1514+
return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName, args);
1515+
}
1516+
else {
1517+
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
1518+
@Override
1519+
public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
1520+
return beanFactory.getBean(beanName, args);
1521+
}
1522+
};
1523+
return doResolveDependency(descriptorToUse, this.beanName, null, null);
1524+
}
1525+
}
1526+
15061527
@Override
15071528
public Object getIfAvailable() throws BeansException {
15081529
if (this.optional) {

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,26 @@ public void testObjectFactorySerialization() throws Exception {
10471047
}
10481048

10491049
@Test
1050-
public void testSmartObjectFactoryInjection() {
1050+
public void testSmartObjectFactoryInjectionWithPrototype() {
1051+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
1052+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
1053+
bpp.setBeanFactory(bf);
1054+
bf.addBeanPostProcessor(bpp);
1055+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
1056+
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
1057+
tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
1058+
bf.registerBeanDefinition("testBean", tbd);
1059+
1060+
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
1061+
assertEquals(bf.getBean("testBean"), bean.getTestBean());
1062+
assertEquals(bf.getBean("testBean", "myName"), bean.getTestBean("myName"));
1063+
assertEquals(bf.getBean("testBean"), bean.getOptionalTestBean());
1064+
assertEquals(bf.getBean("testBean"), bean.getUniqueTestBean());
1065+
bf.destroySingletons();
1066+
}
1067+
1068+
@Test
1069+
public void testSmartObjectFactoryInjectionWithSingletonTarget() {
10511070
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
10521071
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
10531072
bpp.setBeanFactory(bf);
@@ -2557,6 +2576,10 @@ public TestBean getTestBean() {
25572576
return this.testBeanFactory.getObject();
25582577
}
25592578

2579+
public TestBean getTestBean(String name) {
2580+
return this.testBeanFactory.getObject(name);
2581+
}
2582+
25602583
public TestBean getOptionalTestBean() {
25612584
return this.testBeanFactory.getIfAvailable();
25622585
}

0 commit comments

Comments
 (0)