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]]