From 7e7018efe3ec1585e58f254166e0364230a21af7 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 | 23 ++-- .../beans/factory/BeanFactoryUtilsTests.java | 105 +++++++++++++++++- 2 files changed, 118 insertions(+), 10 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 b77845cb9be3..4d2fed1e981b 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-2018 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. @@ -41,20 +41,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 */ @@ -79,7 +81,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) { @@ -90,7 +92,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 */ @@ -187,7 +189,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 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 ab1181ba5f66..904a127d5040 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-2017 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 { @@ -319,4 +320,106 @@ public void testIntDependencies() { assertTrue(Arrays.equals(new String[] { "buffer" }, deps)); } + @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); + assertSame(bean, beans.get("bean")); + assertSame(fb1.getObject(), beans.get("fb1")); + assertTrue(beans.get("fb2") instanceof TestBean); + assertTrue(beans.get("sfb1") instanceof TestBean); + assertTrue(beans.get("sfb2") instanceof TestBean); + assertTrue(beans.get("sfb3") instanceof TestBean); + assertTrue(beans.get("sfb4") instanceof TestBean); + + assertEquals(7, lbf.getBeanDefinitionCount()); + assertTrue(lbf.getBean("bean") instanceof TestBean); + assertTrue(lbf.getBean("&fb1") instanceof FactoryBean); + assertTrue(lbf.getBean("&fb2") instanceof FactoryBean); + assertTrue(lbf.getBean("&sfb1") instanceof SmartFactoryBean); + assertTrue(lbf.getBean("&sfb2") instanceof SmartFactoryBean); + assertTrue(lbf.getBean("&sfb3") instanceof SmartFactoryBean); + assertTrue(lbf.getBean("&sfb4") instanceof SmartFactoryBean); + + assertTrue(lbf.isSingleton("bean")); + assertTrue(lbf.isSingleton("fb1")); + assertTrue(lbf.isSingleton("fb2")); + assertTrue(lbf.isSingleton("sfb1")); + assertTrue(lbf.isSingleton("sfb2")); + assertTrue(lbf.isSingleton("sfb3")); + assertTrue(lbf.isSingleton("sfb4")); + + assertTrue(lbf.isSingleton("&fb1")); + assertFalse(lbf.isSingleton("&fb2")); + assertTrue(lbf.isSingleton("&sfb1")); + assertTrue(lbf.isSingleton("&sfb2")); + assertFalse(lbf.isSingleton("&sfb3")); + assertFalse(lbf.isSingleton("&sfb4")); + + assertFalse(lbf.isPrototype("bean")); + assertFalse(lbf.isPrototype("fb1")); + assertFalse(lbf.isPrototype("fb2")); + assertFalse(lbf.isPrototype("sfb1")); + assertFalse(lbf.isPrototype("sfb2")); + assertFalse(lbf.isPrototype("sfb3")); + assertFalse(lbf.isPrototype("sfb4")); + + assertFalse(lbf.isPrototype("&fb1")); + assertTrue(lbf.isPrototype("&fb2")); + assertTrue(lbf.isPrototype("&sfb1")); + assertFalse(lbf.isPrototype("&sfb2")); + assertTrue(lbf.isPrototype("&sfb3")); + assertTrue(lbf.isPrototype("&sfb4")); + } + + + 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; + } + } + }