JavaSpringbean对象封送取决于XML中给定的顺序?
我这里有一个简单的示例程序:
package com.test;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) throws InterruptedException {
try {
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("TestBeanFile.xml");
Object o = context.getBean("AInstance");
}
catch (Throwable e) {
e.printStackTrace();
}
Thread.sleep(Long.MAX_VALUE);
}
private static class A implements InitializingBean {
private B myB;
public A() {
System.out.println("A constructor");
}
public void setB(B aB) {
myB = aB;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("A aps");
}
}
private static class B implements Runnable, InitializingBean {
private C myC;
private volatile boolean exit = false;
public B() {
System.out.println("B constructor");
}
public void setC(C aC) {
myC = aC;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("B aps");
if (myC == null) throw new IllegalArgumentException("C cannot be null");
new Thread(this).start();
}
public void exit() {
exit = true;
}
@Override
public void run() {
while (!exit) {
System.out.println(myC.getValue());
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private static class C implements InitializingBean {
private String value = "C's value";
public C() {
System.out.println("C constructor");
}
public String getValue() {
return value;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("C aps");
}
}
}
这里有一个简单的XML来加载它们,它故意给a类指定了一个不正确的完全限定名。这意味着C B和A不能被构造
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd">
<bean id="CInstance" class = "com.test.Main.C" />
<bean id="BInstance" class = "com.test.Main.B">
<property name="C" ref="CInstance" />
</bean>
<bean id="AInstance" class = "com.test.Main.A1">
<property name="B" ref="BInstance"/>
</bean>
</beans>
当您使用上述XML运行此应用程序时,C和B确实得到构造,但A没有。它将产生以下输出(在抛出异常时忽略堆栈跟踪,但我向您保证不会构造异常):
C constructor
C aps
B constructor
B aps
C's value
C's value
C's value
....... // one a second
如果将XML修改为如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd">
<bean id="AInstance" class = "com.test.Main.A1">
<property name="B" ref="BInstance"/>
</bean>
<bean id="BInstance" class = "com.test.Main.B">
<property name="C" ref="CInstance" />
</bean>
<bean id="CInstance" class = "com.test.Main.C" />
</beans>
您得到的唯一输出是堆栈跟踪,而B和C对象根本不会被构造
这好像是春天的虫子。我不明白为什么对象的封送以及运行的构造函数/AfterPropertieSet方法依赖于它们在XML中的出现顺序。这对我来说意味着,当我有一些类在它们的构造函数/after properties set方法中构造线程时,如果我不注意如何在XML中对元素排序,那么如果在本例中,我对它们的唯一引用是A类,那么最终可能会泄漏资源。因为它在这两种情况下都失败了
那么,这是一个bug,还是一些我不了解的特性?如果这是一个特性,他们有什么理由使封送的顺序依赖于XML文件,而不是bean定义的对象图
# 1 楼答案
Spring首先读取
<bean>
和XML配置中的其他元素,并创建相应的BeanDefinition
对象来定义它们然后它必须初始化它们。要做到这一点,它必须决定订单。初始化顺序是未定义的,除非一个bean依赖于另一个bean,或者存在
@Order
、Ordered
的实现和其他一些(取决于具体情况)的混合在第一个示例中,
CInstance
首先初始化。然后BInstance
被初始化。其初始化的一部分涉及调用其afterPropertiesSet()
方法,该方法启动一个非守护进程线程。然后Spring尝试初始化AInstance
并失败在第二个示例中,Spring首先尝试初始化
AInstance
。它立即失败,没有任何更改来启动第二个线程注意,在一个声明中
尽管
AInstance
依赖于BInstance
,但在调用任何属性设置程序将BInstance
分配给B
之前,需要初始化实际实例。因此Spring通过实例化类开始AInstance
的初始化。如果失败,则整个上下文刷新将失败。如果通过,它将初始化BInstance
bean并使用它设置B
属性