diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 5b5bfe951b7..0412c896ca8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1055,7 +1055,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp String scopeName = mbd.getScope(); Scope scope = this.scopes.get(scopeName); if (scope == null) { - throw new IllegalStateException("No Scope SPI registered for scope '" + scopeName + "'"); + throw new IllegalStateException("No Scope SPI registered for scope name '" + scopeName + "'"); } Object bean = scope.remove(beanName); if (bean != null) { @@ -1578,7 +1578,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp // A bean with a custom scope... Scope scope = this.scopes.get(mbd.getScope()); if (scope == null) { - throw new IllegalStateException("No Scope registered for scope '" + mbd.getScope() + "'"); + throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'"); } scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd index 9a9cb52bb11..8dfe6d25f2e 100644 --- a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd +++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd @@ -31,8 +31,8 @@ (or an ancestor factory). As alternative to bean references, "inner bean definitions" can be used. - Singleton flags of such inner bean definitions are effectively ignored: - inner beans are typically anonymous prototypes. + Such inner beans do not have an independent lifecycle; they are typically + anonymous nested objects that share the scope of their containing bean. There is also support for lists, sets, maps, and java.util.Properties as bean property types or constructor argument types. @@ -113,16 +113,19 @@ @@ -130,7 +133,9 @@ @@ -305,10 +310,10 @@ service objects. Further scopes, such as "request" or "session", might be supported by extended bean factories (e.g. in a web environment). - Inner bean definitions inherit the singleton status of their containing - bean definition, unless explicitly specified: The inner bean will be a + Inner bean definitions inherit the scope of their containing bean + definition, unless explicitly specified: The inner bean will be a singleton if the containing bean is a singleton, and a prototype if - the containing bean has any other scope. + the containing bean is a prototype, etc. ]]> @@ -328,13 +333,15 @@ @@ -344,7 +351,7 @@ Controls whether bean properties are "autowired". This is an automagical process in which bean references don't need to be coded explicitly in the XML bean definition file, but rather the - Spring container works out dependencies. + Spring container works out dependencies. The effective default is "no". There are 4 modes: @@ -379,7 +386,10 @@ elements, always override autowiring. Note: This attribute will not be inherited by child bean definitions. - Hence, it needs to be specified per concrete bean definition. + Hence, it needs to be specified per concrete bean definition. It can be + shared through the 'default-autowire' attribute at the 'beans' level + and potentially inherited from outer 'beans' defaults in case of nested + 'beans' sections (e.g. with different profiles). ]]> diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index acc2d1eee3a..eac8dc9ab05 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -713,7 +713,7 @@ public class DefaultListableBeanFactoryTests { RootBeanDefinition parentDefinition = new RootBeanDefinition(TestBean.class); parentDefinition.setAbstract(true); parentDefinition.getPropertyValues().add("name", EXPECTED_NAME); - parentDefinition.getPropertyValues().add("age", new Integer(EXPECTED_AGE)); + parentDefinition.getPropertyValues().add("age", EXPECTED_AGE); ChildBeanDefinition childDefinition = new ChildBeanDefinition("alias"); @@ -1201,7 +1201,7 @@ public class DefaultListableBeanFactoryTests { RootBeanDefinition rbd = new RootBeanDefinition(PropertiesFactoryBean.class); MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.add("locations", new String[] {"#{foo}"}); + pvs.add("locations", new String[]{"#{foo}"}); rbd.setPropertyValues(pvs); bf.registerBeanDefinition("myProperties", rbd); Properties properties = (Properties) bf.getBean("myProperties"); @@ -2264,32 +2264,6 @@ public class DefaultListableBeanFactoryTests { assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 3000); } - /** - * @Test - * public void testPrototypeCreationWithConstructorArgumentsIsFastEnough2() throws Exception { - * if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) { - * // Skip this test: Trace logging blows the time limit. - * return; - * } - * DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); - * Constructor ctor = TestBean.class.getConstructor(String.class, int.class); - * Method setBeanNameMethod = TestBean.class.getMethod("setBeanName", String.class); - * Method setBeanFactoryMethod = TestBean.class.getMethod("setBeanFactory", BeanFactory.class); - * StopWatch sw = new StopWatch(); - * sw.start("prototype"); - * for (int i = 0; i < 100000; i++) { - * TestBean tb = ctor.newInstance("juergen", 99); - * setBeanNameMethod.invoke(tb, "test"); - * setBeanFactoryMethod.invoke(tb, lbf); - * assertEquals("juergen", tb.getName()); - * assertEquals(99, tb.getAge()); - * } - * sw.stop(); - * // System.out.println(sw.getTotalTimeMillis()); - * assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 1500); - * } - */ - @Test public void testPrototypeCreationWithResolvedConstructorArgumentsIsFastEnough() { Assume.group(TestGroup.PERFORMANCE); @@ -2334,31 +2308,6 @@ public class DefaultListableBeanFactoryTests { assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 3000); } - /** - * public void testPrototypeCreationWithPropertiesIsFastEnough2() throws Exception { - * if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) { - * // Skip this test: Trace logging blows the time limit. - * return; - * } - * DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); - * StopWatch sw = new StopWatch(); - * Method setBeanNameMethod = TestBean.class.getMethod("setBeanName", String.class); - * Method setBeanFactoryMethod = TestBean.class.getMethod("setBeanFactory", BeanFactory.class); - * Method setNameMethod = TestBean.class.getMethod("setName", String.class); - * Method setAgeMethod = TestBean.class.getMethod("setAge", int.class); - * sw.start("prototype"); - * for (int i = 0; i < 100000; i++) { - * TestBean tb = TestBean.class.newInstance(); - * setBeanNameMethod.invoke(tb, "test"); - * setBeanFactoryMethod.invoke(tb, lbf); - * setNameMethod.invoke(tb, "juergen"); - * setAgeMethod.invoke(tb, 99); - * } - * sw.stop(); - * // System.out.println(sw.getTotalTimeMillis()); - * assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 750); - * } - */ @Test public void testPrototypeCreationWithResolvedPropertiesIsFastEnough() { Assume.group(TestGroup.PERFORMANCE); @@ -2439,10 +2388,41 @@ public class DefaultListableBeanFactoryTests { return bean; } }); - BeanWithDestroyMethod.closed = false; + BeanWithDestroyMethod.closeCount = 0; + lbf.preInstantiateSingletons(); + lbf.destroySingletons(); + assertEquals("Destroy methods invoked", 1, BeanWithDestroyMethod.closeCount); + } + + @Test + public void testDestroyMethodOnInnerBean() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition innerBd = new RootBeanDefinition(BeanWithDestroyMethod.class); + innerBd.setDestroyMethodName("close"); + RootBeanDefinition bd = new RootBeanDefinition(BeanWithDestroyMethod.class); + bd.setDestroyMethodName("close"); + bd.getPropertyValues().add("inner", innerBd); + lbf.registerBeanDefinition("test", bd); + BeanWithDestroyMethod.closeCount = 0; + lbf.preInstantiateSingletons(); + lbf.destroySingletons(); + assertEquals("Destroy methods invoked", 2, BeanWithDestroyMethod.closeCount); + } + + @Test + public void testDestroyMethodOnInnerBeanAsPrototype() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition innerBd = new RootBeanDefinition(BeanWithDestroyMethod.class); + innerBd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + innerBd.setDestroyMethodName("close"); + RootBeanDefinition bd = new RootBeanDefinition(BeanWithDestroyMethod.class); + bd.setDestroyMethodName("close"); + bd.getPropertyValues().add("inner", innerBd); + lbf.registerBeanDefinition("test", bd); + BeanWithDestroyMethod.closeCount = 0; lbf.preInstantiateSingletons(); lbf.destroySingletons(); - assertTrue("Destroy method invoked", BeanWithDestroyMethod.closed); + assertEquals("Destroy methods invoked", 1, BeanWithDestroyMethod.closeCount); } @Test @@ -2679,10 +2659,6 @@ public class DefaultListableBeanFactoryTests { verify(r3, never()).resolveStringValue(isNull(String.class)); } - - static class A { } - static class B { } - /** * Test that by-type bean lookup caching is working effectively by searching for a * bean of type B 10K times within a container having 1K additional beans of type A. @@ -2693,24 +2669,29 @@ public class DefaultListableBeanFactoryTests { * under the 1000 ms timeout, usually ~= 300ms. With caching removed and on the same * hardware the method will take ~13000 ms. See SPR-6870. */ - @Test(timeout=1000) + @Test(timeout = 1000) public void testByTypeLookupIsFastEnough() { Assume.group(TestGroup.PERFORMANCE); DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); for (int i = 0; i < 1000; i++) { - bf.registerBeanDefinition("a"+i, new RootBeanDefinition(A.class)); + bf.registerBeanDefinition("a" + i, new RootBeanDefinition(A.class)); } bf.registerBeanDefinition("b", new RootBeanDefinition(B.class)); bf.freezeConfiguration(); - for (int i=0; i<10000; i++) { + for (int i = 0; i < 10000; i++) { bf.getBean(B.class); } } + static class A { } + + static class B { } + + public static class NoDependencies { private NoDependencies() { @@ -2816,10 +2797,16 @@ public class DefaultListableBeanFactoryTests { public static class BeanWithDestroyMethod { - private static boolean closed; + private static int closeCount = 0; + + private BeanWithDestroyMethod inner; + + public void setInner(BeanWithDestroyMethod inner) { + this.inner = inner; + } public void close() { - closed = true; + closeCount++; } } @@ -2981,7 +2968,6 @@ public class DefaultListableBeanFactoryTests { private FactoryBean factoryBean; - public final FactoryBean getFactoryBean() { return this.factoryBean; } diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index edde31260a2..336029d27ad 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -2663,10 +2663,17 @@ so-called __inner bean__. ---- -An inner bean definition does not require a defined id or name; the container ignores -these values. It also ignores the `scope` flag. Inner beans are __always__ anonymous and -they are __always__ created with the outer bean. It is __not__ possible to inject inner -beans into collaborating beans other than into the enclosing bean. +An inner bean definition does not require a defined id or name; if specified, the container +does not use such a value as an identifier. The container also ignores the `scope` flag on +creation: Inner beans are __always__ anonymous and they are __always__ created with the outer +bean. It is __not__ possible to inject inner beans into collaborating beans other than into +the enclosing bean or to access them independently. + +As a corner case, it is possible to receive destruction callbacks from a custom scope, e.g. +for a request-scoped inner bean contained within a singleton bean: The creation of the inner +bean instance will be tied to its containing bean, but destruction callbacks allow it to +participate in the request scope's lifecycle. This is not a common scenario; inner beans +typically simply share their containing bean's scope. [[beans-collection-elements]]