From 8ffe0dc65775322d9dffc1efd45d399bf1599f1b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 4 Aug 2020 15:13:10 +0200 Subject: [PATCH] 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 --- .../support/StaticListableBeanFactory.java | 24 ++-- .../beans/factory/BeanFactoryUtilsTests.java | 105 +++++++++++++++++- 2 files changed, 118 insertions(+), 11 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 7d77a45b0d71..6262215b37a0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,20 +45,22 @@ /** * Static {@link org.springframework.beans.factory.BeanFactory} implementation - * which allows to register existing singleton instances programmatically. - * Does not have support for prototype beans or aliases. + * which allows one to register existing singleton instances programmatically. * - *

Serves as example for a simple implementation of the + *

Does not have support for prototype beans or aliases. + * + *

Serves as an example for a simple implementation of the * {@link org.springframework.beans.factory.ListableBeanFactory} interface, * managing existing bean instances rather than creating new ones based on bean * definitions, and not implementing any extended SPI interfaces (such as * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}). * - *

For a full-fledged factory based on bean definitions, have a look - * at {@link DefaultListableBeanFactory}. + *

For a full-fledged factory based on bean definitions, have a look at + * {@link DefaultListableBeanFactory}. * * @author Rod Johnson * @author Juergen Hoeller + * @author Sam Brannen * @since 06.01.2003 * @see DefaultListableBeanFactory */ @@ -83,7 +85,7 @@ public StaticListableBeanFactory() { * or {@link java.util.Collections#emptyMap()} for a dummy factory which * enforces operating against an empty set of beans. * @param beans a {@code Map} for holding this factory's beans, with the - * bean name String as key and the corresponding singleton object as value + * bean name as key and the corresponding singleton object as value * @since 4.3 */ public StaticListableBeanFactory(Map beans) { @@ -94,7 +96,7 @@ public StaticListableBeanFactory(Map beans) { /** * Add a new singleton bean. - * Will overwrite any existing instance for the given name. + *

Will overwrite any existing instance for the given name. * @param name the name of the bean * @param bean the bean instance */ @@ -262,7 +264,10 @@ public boolean containsBean(String name) { public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { Object bean = getBean(name); // In case of FactoryBean, return singleton status of created object. - return (bean instanceof FactoryBean && ((FactoryBean) bean).isSingleton()); + if (bean instanceof FactoryBean) { + return ((FactoryBean) bean).isSingleton(); + } + return true; } @Override @@ -337,7 +342,6 @@ public String[] getBeanNamesForType(@Nullable ResolvableType type) { return getBeanNamesForType(type, true, true); } - @Override public String[] getBeanNamesForType(@Nullable ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java index 104556d614c5..a84a028eae55 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams + * @author Sam Brannen * @since 04.07.2003 */ public class BeanFactoryUtilsTests { @@ -323,4 +324,106 @@ public void testIntDependencies() { assertThat(Arrays.equals(new String[] { "buffer" }, deps)).isTrue(); } + @Test + public void isSingletonAndIsPrototypeWithStaticFactory() { + StaticListableBeanFactory lbf = new StaticListableBeanFactory(); + TestBean bean = new TestBean(); + DummyFactory fb1 = new DummyFactory(); + DummyFactory fb2 = new DummyFactory(); + fb2.setSingleton(false); + TestBeanSmartFactoryBean sfb1 = new TestBeanSmartFactoryBean(true, true); + TestBeanSmartFactoryBean sfb2 = new TestBeanSmartFactoryBean(true, false); + TestBeanSmartFactoryBean sfb3 = new TestBeanSmartFactoryBean(false, true); + TestBeanSmartFactoryBean sfb4 = new TestBeanSmartFactoryBean(false, false); + lbf.addBean("bean", bean); + lbf.addBean("fb1", fb1); + lbf.addBean("fb2", fb2); + lbf.addBean("sfb1", sfb1); + lbf.addBean("sfb2", sfb2); + lbf.addBean("sfb3", sfb3); + lbf.addBean("sfb4", sfb4); + + Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, ITestBean.class, true, true); + assertThat(beans.get("bean")).isSameAs(bean); + assertThat(beans.get("fb1")).isSameAs(fb1.getObject()); + assertThat(beans.get("fb2")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb1")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb2")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb3")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb4")).isInstanceOf(TestBean.class); + + assertThat(lbf.getBeanDefinitionCount()).isEqualTo(7); + assertThat(lbf.getBean("bean")).isInstanceOf(TestBean.class); + assertThat(lbf.getBean("&fb1")).isInstanceOf(FactoryBean.class); + assertThat(lbf.getBean("&fb2")).isInstanceOf(FactoryBean.class); + assertThat(lbf.getBean("&sfb1")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb2")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb3")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb4")).isInstanceOf(SmartFactoryBean.class); + + assertThat(lbf.isSingleton("bean")).isTrue(); + assertThat(lbf.isSingleton("fb1")).isTrue(); + assertThat(lbf.isSingleton("fb2")).isTrue(); + assertThat(lbf.isSingleton("sfb1")).isTrue(); + assertThat(lbf.isSingleton("sfb2")).isTrue(); + assertThat(lbf.isSingleton("sfb3")).isTrue(); + assertThat(lbf.isSingleton("sfb4")).isTrue(); + + assertThat(lbf.isSingleton("&fb1")).isTrue(); + assertThat(lbf.isSingleton("&fb2")).isFalse(); + assertThat(lbf.isSingleton("&sfb1")).isTrue(); + assertThat(lbf.isSingleton("&sfb2")).isTrue(); + assertThat(lbf.isSingleton("&sfb3")).isFalse(); + assertThat(lbf.isSingleton("&sfb4")).isFalse(); + + assertThat(lbf.isPrototype("bean")).isFalse(); + assertThat(lbf.isPrototype("fb1")).isFalse(); + assertThat(lbf.isPrototype("fb2")).isFalse(); + assertThat(lbf.isPrototype("sfb1")).isFalse(); + assertThat(lbf.isPrototype("sfb2")).isFalse(); + assertThat(lbf.isPrototype("sfb3")).isFalse(); + assertThat(lbf.isPrototype("sfb4")).isFalse(); + + assertThat(lbf.isPrototype("&fb1")).isFalse(); + assertThat(lbf.isPrototype("&fb2")).isTrue(); + assertThat(lbf.isPrototype("&sfb1")).isTrue(); + assertThat(lbf.isPrototype("&sfb2")).isFalse(); + assertThat(lbf.isPrototype("&sfb3")).isTrue(); + assertThat(lbf.isPrototype("&sfb4")).isTrue(); + } + + + static class TestBeanSmartFactoryBean implements SmartFactoryBean { + + private final TestBean testBean = new TestBean("enigma", 42); + private final boolean singleton; + private final boolean prototype; + + TestBeanSmartFactoryBean(boolean singleton, boolean prototype) { + this.singleton = singleton; + this.prototype = prototype; + } + + @Override + public boolean isSingleton() { + return this.singleton; + } + + @Override + public boolean isPrototype() { + return this.prototype; + } + + @Override + public Class getObjectType() { + return TestBean.class; + } + + public TestBean getObject() throws Exception { + // We don't really care if the actual instance is a singleton or prototype + // for the tests that use this factory. + return this.testBean; + } + } + }