有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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) 个答案

  1. # 1 楼答案

    Spring首先读取<bean>和XML配置中的其他元素,并创建相应的BeanDefinition对象来定义它们

    然后它必须初始化它们。要做到这一点,它必须决定订单。初始化顺序是未定义的,除非一个bean依赖于另一个bean,或者存在@OrderOrdered的实现和其他一些(取决于具体情况)的混合

    在第一个示例中,CInstance首先初始化。然后BInstance被初始化。其初始化的一部分涉及调用其afterPropertiesSet()方法,该方法启动一个非守护进程线程。然后Spring尝试初始化AInstance并失败

    在第二个示例中,Spring首先尝试初始化AInstance。它立即失败,没有任何更改来启动第二个线程

    注意,在一个声明中

    <bean id="AInstance" class="com.example.Spring.A1">
        <property name="B" ref="BInstance" />
    </bean>
    

    尽管AInstance依赖于BInstance,但在调用任何属性设置程序将BInstance分配给B之前,需要初始化实际实例。因此Spring通过实例化类开始AInstance的初始化。如果失败,则整个上下文刷新将失败。如果通过,它将初始化BInstancebean并使用它设置B属性