Skip to content

Commit fd5dbdd

Browse files
committed
BeanFactory supports bean creation arguments for by-type lookup as well
Issue: SPR-11235
1 parent 8dbfa80 commit fd5dbdd

File tree

7 files changed

+187
-15
lines changed

7 files changed

+187
-15
lines changed

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

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -181,6 +181,27 @@ public interface BeanFactory {
181181
*/
182182
Object getBean(String name, Object... args) throws BeansException;
183183

184+
/**
185+
* Return an instance, which may be shared or independent, of the specified bean.
186+
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
187+
* overriding the specified default arguments (if any) in the bean definition.
188+
* @param requiredType type the bean must match; can be an interface or superclass.
189+
* {@code null} is disallowed.
190+
* <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
191+
* but may also be translated into a conventional by-name lookup based on the name
192+
* of the given type. For more extensive retrieval operations across sets of beans,
193+
* use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
194+
* @param args arguments to use if creating a prototype using explicit arguments to a
195+
* static factory method. It is invalid to use a non-null args value in any other case.
196+
* @return an instance of the bean
197+
* @throws NoSuchBeanDefinitionException if there is no such bean definition
198+
* @throws BeanDefinitionStoreException if arguments have been given but
199+
* the affected bean isn't a prototype
200+
* @throws BeansException if the bean could not be created
201+
* @since 4.1
202+
*/
203+
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
204+
184205
/**
185206
* Does this bean factory contain a bean definition or externally registered singleton
186207
* instance with the given name?

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

+10-5
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
295295

296296
@Override
297297
public <T> T getBean(Class<T> requiredType) throws BeansException {
298+
return getBean(requiredType, (Object[]) null);
299+
}
300+
301+
@Override
302+
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
298303
Assert.notNull(requiredType, "Required type must not be null");
299304
String[] beanNames = getBeanNamesForType(requiredType);
300305
if (beanNames.length > 1) {
@@ -309,25 +314,25 @@ public <T> T getBean(Class<T> requiredType) throws BeansException {
309314
}
310315
}
311316
if (beanNames.length == 1) {
312-
return getBean(beanNames[0], requiredType);
317+
return getBean(beanNames[0], requiredType, args);
313318
}
314319
else if (beanNames.length > 1) {
315320
Map<String, Object> candidates = new HashMap<String, Object>();
316321
for (String beanName : beanNames) {
317-
candidates.put(beanName, getBean(beanName, requiredType));
322+
candidates.put(beanName, getBean(beanName, requiredType, args));
318323
}
319324
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
320325
if (primaryCandidate != null) {
321-
return getBean(primaryCandidate, requiredType);
326+
return getBean(primaryCandidate, requiredType, args);
322327
}
323328
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
324329
if (priorityCandidate != null) {
325-
return getBean(priorityCandidate, requiredType);
330+
return getBean(priorityCandidate, requiredType, args);
326331
}
327332
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
328333
}
329334
else if (getParentBeanFactory() != null) {
330-
return getParentBeanFactory().getBean(requiredType);
335+
return getParentBeanFactory().getBean(requiredType, args);
331336
}
332337
else {
333338
throw new NoSuchBeanDefinitionException(requiredType);

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -133,11 +133,20 @@ else if (beanNames.length > 1) {
133133
public Object getBean(String name, Object... args) throws BeansException {
134134
if (args != null) {
135135
throw new UnsupportedOperationException(
136-
"StaticListableBeanFactory does not support explicit bean creation arguments)");
136+
"StaticListableBeanFactory does not support explicit bean creation arguments");
137137
}
138138
return getBean(name);
139139
}
140140

141+
@Override
142+
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
143+
if (args != null) {
144+
throw new UnsupportedOperationException(
145+
"StaticListableBeanFactory does not support explicit bean creation arguments");
146+
}
147+
return getBean(requiredType);
148+
}
149+
141150
@Override
142151
public boolean containsBean(String name) {
143152
return this.beans.containsKey(name);

spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

+122-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1343,6 +1343,16 @@ public void testGetBeanByTypeWithNoneFound() {
13431343
lbf.getBean(TestBean.class);
13441344
}
13451345

1346+
@Test
1347+
public void testGetBeanByTypeDefinedInParent() {
1348+
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
1349+
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
1350+
parent.registerBeanDefinition("bd1", bd1);
1351+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parent);
1352+
TestBean bean = lbf.getBean(TestBean.class);
1353+
assertThat(bean.getBeanName(), equalTo("bd1"));
1354+
}
1355+
13461356
@Test(expected=NoUniqueBeanDefinitionException.class)
13471357
public void testGetBeanByTypeWithAmbiguity() {
13481358
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
@@ -1449,6 +1459,95 @@ public void testGetBeanByTypeFiltersOutNonAutowireCandidates() {
14491459
}
14501460
}
14511461

1462+
@Test(expected = NoSuchBeanDefinitionException.class)
1463+
public void testGetBeanByTypeInstanceWithNoneFound() {
1464+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1465+
lbf.getBean(ConstructorDependency.class, 42);
1466+
}
1467+
1468+
@Test
1469+
public void testGetBeanByTypeInstanceDefinedInParent() {
1470+
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
1471+
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
1472+
parent.registerBeanDefinition("bd1", bd1);
1473+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parent);
1474+
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class, 42);
1475+
assertThat(bean.beanName, equalTo("bd1"));
1476+
assertThat(bean.spouseAge, equalTo(42));
1477+
}
1478+
1479+
@Test
1480+
public void testGetBeanByTypeInstanceWithAmbiguity() {
1481+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1482+
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
1483+
RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class);
1484+
bd2.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
1485+
bd2.getConstructorArgumentValues().addGenericArgumentValue("43");
1486+
1487+
lbf.registerBeanDefinition("bd1", bd1);
1488+
lbf.registerBeanDefinition("bd2", bd2);
1489+
1490+
thrown.expect(NoUniqueBeanDefinitionException.class);
1491+
lbf.getBean(ConstructorDependency.class, 42);
1492+
}
1493+
1494+
@Test
1495+
public void testGetBeanByTypeInstanceWithPrimary() throws Exception {
1496+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1497+
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
1498+
RootBeanDefinition bd2 = createConstructorDependencyBeanDefinition(43);
1499+
bd2.setPrimary(true);
1500+
lbf.registerBeanDefinition("bd1", bd1);
1501+
lbf.registerBeanDefinition("bd2", bd2);
1502+
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class, 42);
1503+
assertThat(bean.beanName, equalTo("bd2"));
1504+
assertThat(bean.spouseAge, equalTo(42));
1505+
}
1506+
1507+
@Test
1508+
public void testGetBeanByTypeInstanceWithMultiplePrimary() throws Exception {
1509+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1510+
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
1511+
RootBeanDefinition bd2 = createConstructorDependencyBeanDefinition(43);
1512+
bd1.setPrimary(true);
1513+
bd2.setPrimary(true);
1514+
1515+
lbf.registerBeanDefinition("bd1", bd1);
1516+
lbf.registerBeanDefinition("bd2", bd2);
1517+
thrown.expect(NoUniqueBeanDefinitionException.class);
1518+
thrown.expectMessage(containsString("more than one 'primary'"));
1519+
lbf.getBean(ConstructorDependency.class, 42);
1520+
}
1521+
1522+
@Test
1523+
public void testGetBeanByTypeInstanceFiltersOutNonAutowireCandidates() {
1524+
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1525+
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
1526+
RootBeanDefinition bd2 = createConstructorDependencyBeanDefinition(43);
1527+
RootBeanDefinition na1 = createConstructorDependencyBeanDefinition(21);
1528+
na1.setAutowireCandidate(false);
1529+
1530+
lbf.registerBeanDefinition("bd1", bd1);
1531+
lbf.registerBeanDefinition("na1", na1);
1532+
ConstructorDependency actual = lbf.getBean(ConstructorDependency.class, 42); // na1 was filtered
1533+
assertThat(actual.beanName, equalTo("bd1"));
1534+
1535+
lbf.registerBeanDefinition("bd2", bd2);
1536+
try {
1537+
lbf.getBean(TestBean.class, 67);
1538+
fail("Should have thrown NoSuchBeanDefinitionException");
1539+
} catch (NoSuchBeanDefinitionException ex) {
1540+
// expected
1541+
}
1542+
}
1543+
1544+
private RootBeanDefinition createConstructorDependencyBeanDefinition(int age) {
1545+
RootBeanDefinition bd1 = new RootBeanDefinition(ConstructorDependency.class);
1546+
bd1.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
1547+
bd1.getConstructorArgumentValues().addGenericArgumentValue(String.valueOf(age));
1548+
return bd1;
1549+
}
1550+
14521551
@Test
14531552
public void testAutowireBeanByType() {
14541553
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
@@ -2525,18 +2624,31 @@ private NoDependencies() {
25252624
}
25262625

25272626

2528-
public static class ConstructorDependency {
2627+
public static class ConstructorDependency implements BeanNameAware {
25292628

25302629
public TestBean spouse;
25312630

2631+
public int spouseAge;
2632+
2633+
private String beanName;
2634+
25322635
public ConstructorDependency(TestBean spouse) {
25332636
this.spouse = spouse;
25342637
}
25352638

2639+
public ConstructorDependency(int spouseAge) {
2640+
this.spouseAge = spouseAge;
2641+
}
2642+
25362643
@SuppressWarnings("unused")
25372644
private ConstructorDependency(TestBean spouse, TestBean otherSpouse) {
25382645
throw new IllegalArgumentException("Should never be called");
25392646
}
2647+
2648+
@Override
2649+
public void setBeanName(String name) {
2650+
this.beanName = name;
2651+
}
25402652
}
25412653

25422654

@@ -2879,6 +2991,7 @@ public String getUserName() {
28792991
}
28802992
}
28812993

2994+
28822995
@SuppressWarnings("unused")
28832996
private static class KnowsIfInstantiated {
28842997

@@ -2898,11 +3011,16 @@ public KnowsIfInstantiated() {
28983011

28993012
}
29003013

3014+
29013015
@Priority(5)
2902-
private static class HighPriorityTestBean extends TestBean {}
3016+
private static class HighPriorityTestBean extends TestBean {
3017+
}
3018+
29033019

29043020
@Priority(500)
2905-
private static class LowPriorityTestBean extends TestBean {}
3021+
private static class LowPriorityTestBean extends TestBean {
3022+
}
3023+
29063024

29073025
private static class NullTestBeanFactoryBean<T> implements FactoryBean<TestBean> {
29083026

@@ -2922,5 +3040,4 @@ public boolean isSingleton() {
29223040
}
29233041
}
29243042

2925-
29263043
}

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

+6
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,12 @@ public Object getBean(String name, Object... args) throws BeansException {
974974
return getBeanFactory().getBean(name, args);
975975
}
976976

977+
@Override
978+
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
979+
assertBeanFactoryActive();
980+
return getBeanFactory().getBean(requiredType, args);
981+
}
982+
977983
@Override
978984
public boolean containsBean(String name) {
979985
return getBeanFactory().containsBean(name);

spring-context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,20 @@ public <T> T getBean(Class<T> requiredType) throws BeansException {
134134
public Object getBean(String name, Object... args) throws BeansException {
135135
if (args != null) {
136136
throw new UnsupportedOperationException(
137-
"SimpleJndiBeanFactory does not support explicit bean creation arguments)");
137+
"SimpleJndiBeanFactory does not support explicit bean creation arguments");
138138
}
139139
return getBean(name);
140140
}
141141

142+
@Override
143+
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
144+
if (args != null) {
145+
throw new UnsupportedOperationException(
146+
"SimpleJndiBeanFactory does not support explicit bean creation arguments");
147+
}
148+
return getBean(requiredType);
149+
}
150+
142151
@Override
143152
public boolean containsBean(String name) {
144153
if (this.singletonObjects.containsKey(name) || this.resourceTypes.containsKey(name)) {

spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -167,6 +167,11 @@ public Object getBean(String name, Object... args) throws BeansException {
167167
return this.beanFactory.getBean(name, args);
168168
}
169169

170+
@Override
171+
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
172+
return this.beanFactory.getBean(requiredType, args);
173+
}
174+
170175
@Override
171176
public boolean containsBean(String name) {
172177
return this.beanFactory.containsBean(name);

0 commit comments

Comments
 (0)