Skip to content

Commit 7a31885

Browse files
committed
Fix bug in StaticListableBeanFactory.isSingleton()
Prior to this commit, StaticListableBeanFactory.isSingleton() returned false for singleton beans unless they were created by a FactoryBean. StaticListableBeanFactory.isSingleton() now properly returns true for all beans not created by a FactoryBean. Closes gh-25522
1 parent 91d1383 commit 7a31885

File tree

2 files changed

+118
-11
lines changed

2 files changed

+118
-11
lines changed

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -45,20 +45,22 @@
4545

4646
/**
4747
* Static {@link org.springframework.beans.factory.BeanFactory} implementation
48-
* which allows to register existing singleton instances programmatically.
49-
* Does not have support for prototype beans or aliases.
48+
* which allows one to register existing singleton instances programmatically.
5049
*
51-
* <p>Serves as example for a simple implementation of the
50+
* <p>Does not have support for prototype beans or aliases.
51+
*
52+
* <p>Serves as an example for a simple implementation of the
5253
* {@link org.springframework.beans.factory.ListableBeanFactory} interface,
5354
* managing existing bean instances rather than creating new ones based on bean
5455
* definitions, and not implementing any extended SPI interfaces (such as
5556
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}).
5657
*
57-
* <p>For a full-fledged factory based on bean definitions, have a look
58-
* at {@link DefaultListableBeanFactory}.
58+
* <p>For a full-fledged factory based on bean definitions, have a look at
59+
* {@link DefaultListableBeanFactory}.
5960
*
6061
* @author Rod Johnson
6162
* @author Juergen Hoeller
63+
* @author Sam Brannen
6264
* @since 06.01.2003
6365
* @see DefaultListableBeanFactory
6466
*/
@@ -83,7 +85,7 @@ public StaticListableBeanFactory() {
8385
* or {@link java.util.Collections#emptyMap()} for a dummy factory which
8486
* enforces operating against an empty set of beans.
8587
* @param beans a {@code Map} for holding this factory's beans, with the
86-
* bean name String as key and the corresponding singleton object as value
88+
* bean name as key and the corresponding singleton object as value
8789
* @since 4.3
8890
*/
8991
public StaticListableBeanFactory(Map<String, Object> beans) {
@@ -94,7 +96,7 @@ public StaticListableBeanFactory(Map<String, Object> beans) {
9496

9597
/**
9698
* Add a new singleton bean.
97-
* Will overwrite any existing instance for the given name.
99+
* <p>Will overwrite any existing instance for the given name.
98100
* @param name the name of the bean
99101
* @param bean the bean instance
100102
*/
@@ -262,7 +264,10 @@ public boolean containsBean(String name) {
262264
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
263265
Object bean = getBean(name);
264266
// In case of FactoryBean, return singleton status of created object.
265-
return (bean instanceof FactoryBean && ((FactoryBean<?>) bean).isSingleton());
267+
if (bean instanceof FactoryBean) {
268+
return ((FactoryBean<?>) bean).isSingleton();
269+
}
270+
return true;
266271
}
267272

268273
@Override
@@ -337,7 +342,6 @@ public String[] getBeanNamesForType(@Nullable ResolvableType type) {
337342
return getBeanNamesForType(type, true, true);
338343
}
339344

340-
341345
@Override
342346
public String[] getBeanNamesForType(@Nullable ResolvableType type,
343347
boolean includeNonSingletons, boolean allowEagerInit) {

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

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -43,6 +43,7 @@
4343
* @author Rod Johnson
4444
* @author Juergen Hoeller
4545
* @author Chris Beams
46+
* @author Sam Brannen
4647
* @since 04.07.2003
4748
*/
4849
public class BeanFactoryUtilsTests {
@@ -323,4 +324,106 @@ public void testIntDependencies() {
323324
assertThat(Arrays.equals(new String[] { "buffer" }, deps)).isTrue();
324325
}
325326

327+
@Test
328+
public void isSingletonAndIsPrototypeWithStaticFactory() {
329+
StaticListableBeanFactory lbf = new StaticListableBeanFactory();
330+
TestBean bean = new TestBean();
331+
DummyFactory fb1 = new DummyFactory();
332+
DummyFactory fb2 = new DummyFactory();
333+
fb2.setSingleton(false);
334+
TestBeanSmartFactoryBean sfb1 = new TestBeanSmartFactoryBean(true, true);
335+
TestBeanSmartFactoryBean sfb2 = new TestBeanSmartFactoryBean(true, false);
336+
TestBeanSmartFactoryBean sfb3 = new TestBeanSmartFactoryBean(false, true);
337+
TestBeanSmartFactoryBean sfb4 = new TestBeanSmartFactoryBean(false, false);
338+
lbf.addBean("bean", bean);
339+
lbf.addBean("fb1", fb1);
340+
lbf.addBean("fb2", fb2);
341+
lbf.addBean("sfb1", sfb1);
342+
lbf.addBean("sfb2", sfb2);
343+
lbf.addBean("sfb3", sfb3);
344+
lbf.addBean("sfb4", sfb4);
345+
346+
Map<String, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, ITestBean.class, true, true);
347+
assertThat(beans.get("bean")).isSameAs(bean);
348+
assertThat(beans.get("fb1")).isSameAs(fb1.getObject());
349+
assertThat(beans.get("fb2")).isInstanceOf(TestBean.class);
350+
assertThat(beans.get("sfb1")).isInstanceOf(TestBean.class);
351+
assertThat(beans.get("sfb2")).isInstanceOf(TestBean.class);
352+
assertThat(beans.get("sfb3")).isInstanceOf(TestBean.class);
353+
assertThat(beans.get("sfb4")).isInstanceOf(TestBean.class);
354+
355+
assertThat(lbf.getBeanDefinitionCount()).isEqualTo(7);
356+
assertThat(lbf.getBean("bean")).isInstanceOf(TestBean.class);
357+
assertThat(lbf.getBean("&fb1")).isInstanceOf(FactoryBean.class);
358+
assertThat(lbf.getBean("&fb2")).isInstanceOf(FactoryBean.class);
359+
assertThat(lbf.getBean("&sfb1")).isInstanceOf(SmartFactoryBean.class);
360+
assertThat(lbf.getBean("&sfb2")).isInstanceOf(SmartFactoryBean.class);
361+
assertThat(lbf.getBean("&sfb3")).isInstanceOf(SmartFactoryBean.class);
362+
assertThat(lbf.getBean("&sfb4")).isInstanceOf(SmartFactoryBean.class);
363+
364+
assertThat(lbf.isSingleton("bean")).isTrue();
365+
assertThat(lbf.isSingleton("fb1")).isTrue();
366+
assertThat(lbf.isSingleton("fb2")).isTrue();
367+
assertThat(lbf.isSingleton("sfb1")).isTrue();
368+
assertThat(lbf.isSingleton("sfb2")).isTrue();
369+
assertThat(lbf.isSingleton("sfb3")).isTrue();
370+
assertThat(lbf.isSingleton("sfb4")).isTrue();
371+
372+
assertThat(lbf.isSingleton("&fb1")).isTrue();
373+
assertThat(lbf.isSingleton("&fb2")).isFalse();
374+
assertThat(lbf.isSingleton("&sfb1")).isTrue();
375+
assertThat(lbf.isSingleton("&sfb2")).isTrue();
376+
assertThat(lbf.isSingleton("&sfb3")).isFalse();
377+
assertThat(lbf.isSingleton("&sfb4")).isFalse();
378+
379+
assertThat(lbf.isPrototype("bean")).isFalse();
380+
assertThat(lbf.isPrototype("fb1")).isFalse();
381+
assertThat(lbf.isPrototype("fb2")).isFalse();
382+
assertThat(lbf.isPrototype("sfb1")).isFalse();
383+
assertThat(lbf.isPrototype("sfb2")).isFalse();
384+
assertThat(lbf.isPrototype("sfb3")).isFalse();
385+
assertThat(lbf.isPrototype("sfb4")).isFalse();
386+
387+
assertThat(lbf.isPrototype("&fb1")).isFalse();
388+
assertThat(lbf.isPrototype("&fb2")).isTrue();
389+
assertThat(lbf.isPrototype("&sfb1")).isTrue();
390+
assertThat(lbf.isPrototype("&sfb2")).isFalse();
391+
assertThat(lbf.isPrototype("&sfb3")).isTrue();
392+
assertThat(lbf.isPrototype("&sfb4")).isTrue();
393+
}
394+
395+
396+
static class TestBeanSmartFactoryBean implements SmartFactoryBean<TestBean> {
397+
398+
private final TestBean testBean = new TestBean("enigma", 42);
399+
private final boolean singleton;
400+
private final boolean prototype;
401+
402+
TestBeanSmartFactoryBean(boolean singleton, boolean prototype) {
403+
this.singleton = singleton;
404+
this.prototype = prototype;
405+
}
406+
407+
@Override
408+
public boolean isSingleton() {
409+
return this.singleton;
410+
}
411+
412+
@Override
413+
public boolean isPrototype() {
414+
return this.prototype;
415+
}
416+
417+
@Override
418+
public Class<TestBean> getObjectType() {
419+
return TestBean.class;
420+
}
421+
422+
public TestBean getObject() throws Exception {
423+
// We don't really care if the actual instance is a singleton or prototype
424+
// for the tests that use this factory.
425+
return this.testBean;
426+
}
427+
}
428+
326429
}

0 commit comments

Comments
 (0)