bean中的<set>标记列表xml-configuration表现为Set </set>

时间:2013-09-04 20:33:14

标签: java spring

我正在阅读“春季食谱”一书,并尝试用例子来研究春天的“魔法”。这就是我所拥有的。 Bean类 SequenceGenerator

public class SequenceGenerator {
    private List<Object> suffixes;
    //..... 
    public void setSuffixes(List<Object> suffixes) {
        this.suffixes = suffixes;
    }
    public synchronized String getSequence() {
        StringBuffer buffer = new StringBuffer();            
        for (Object suffix : suffixes) {
            buffer.append(suffix);
            buffer.append("-");
        }
        return buffer.toString();
    }
}

主要课程:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("beans.xml");
        SequenceGenerator generator =
                (SequenceGenerator) context.getBean("sequenceGeneratorSet");
        System.out.println(generator.getSequence());
    }
}

Xml config:

<bean id="sequenceGeneratorSet" class="com.apress.springrecipes.sequence.SequenceGenerator">
        <property name="initial" value="100000"/>
        <property name="suffixes">
            <set>
                <value>A</value>
                <value>A</value>
                <bean class="java.net.URL">
                    <constructor-arg value="http" />
                    <constructor-arg value="www.apress.com" />
                    <constructor-arg value="/" />
                </bean>
            </set>
        </property>
    </bean>

在xml中,我有意使用tag来观察结果。我也写了两次相同的值“A”。我注意到Spring为“suffixes”属性注入了ArrayList,作为bean类中的类型定义。但它的行为类似于输出上的设置,只包含一个“A”值。有没有人知道Spring如何在内部解决这个问题?

2 个答案:

答案 0 :(得分:4)

Spring有一大堆方法调用来生成你的上下文。当它解析你的XML上下文时,Spring会生成一个RootBeanDefinition对象来描述你的bean(类,属性等)和PropertyValues对象,这些对象包含属性的名称(<property>)及其对象值。

在这种情况下,它会创建一个ManagedSet

  

用于保存托管Set值的标记集合类,可以使用   包括运行时bean引用(要解析为bean对象)

保留AURL值。

以上是在任何实际bean对象的字段初始化之前完成的(BeanPostProcessors做他们的事情,代理等等),尽管实例本身(默认字段值为null)是创建。您可以通过创建一个空构造函数并在调试时添加断点来看到这一点。

更进一步,在AbstractApplicationContext#refresh()中,finishBeanFactoryInitialization()被调用,初始化最终发生。对于之前创建的每个BeanDefinition和相应的PropertyValues,Spring会在创建您的bean的applyPropertyValues()上调用BeanFactory

对于suffixes字段,Spring会发现预期的类型与实际类型不匹配。 TypeConverterDelegate将确定要使用PropertyEditorConversionServicePropertyValuesManagedSet)转换为实际的Field类型,即。 List。在这种情况下,它使用CustomCollectionEditor。该编辑器使用createCollection()声明为的集合类型调用Field。对你来说,那是List。所以

protected Collection createCollection(Class collectionType, int initialCapacity) {
    if (!collectionType.isInterface()) {
        try {
            return (Collection) collectionType.newInstance();
        }
        catch (Exception ex) {
            throw new IllegalArgumentException(
                    "Could not instantiate collection class [" + collectionType.getName() + "]: " + ex.getMessage());
        }
    }
    else if (List.class.equals(collectionType)) { // US HERE
        return new ArrayList(initialCapacity);
    }
    else if (SortedSet.class.equals(collectionType)) {
        return new TreeSet();
    }
    else {
        return new LinkedHashSet(initialCapacity);
    }
}

它会创建一个ArrayList。对于我们之前的Set中的每个元素,它会尝试在必要时转换元素,然后将其添加到ArrayList。使用BeanWrapperImpl,然后设置属性值。要执行此操作,它会通过反射找到您的setter Method并使用ArrayList调用它。

Spring为每个bean和上下文中声明的每个属性执行相同的逻辑。

答案 1 :(得分:0)

所有的魔法都发生在org.springframework.beans.propertyeditors.CustomCollectionEditor中。 它负责从上下文提供的对象(在您的示例中设置)创建特定类型的属性(在您的情况下为List):

public void setValue(Object value) {
    if (value == null && this.nullAsEmptyCollection) {
        super.setValue(createCollection(this.collectionType, 0));
    }
    else if (value == null || (this.collectionType.isInstance(value) && !alwaysCreateNewCollection())) {
        // Use the source value as-is, as it matches the target type.
        super.setValue(value);
    }
    else if (value instanceof Collection) {
        // Convert Collection elements.
        Collection source = (Collection) value;
        Collection target = createCollection(this.collectionType, source.size());
        for (Object elem : source) {
            target.add(convertElement(elem));
        }
        super.setValue(target);
    }
    else if (value.getClass().isArray()) {
        // Convert array elements to Collection elements.
        int length = Array.getLength(value);
        Collection target = createCollection(this.collectionType, length);
        for (int i = 0; i < length; i++) {
            target.add(convertElement(Array.get(value, i)));
        }
        super.setValue(target);
    }
    else {
        // A plain value: convert it to a Collection with a single element.
        Collection target = createCollection(this.collectionType, 1);
        target.add(convertElement(value));
        super.setValue(target);
    }
}


protected Collection createCollection(Class collectionType, int initialCapacity) {
    if (!collectionType.isInterface()) {
        try {
            return (Collection) collectionType.newInstance();
        }
        catch (Exception ex) {
            throw new IllegalArgumentException(
                    "Could not instantiate collection class [" + collectionType.getName() + "]: " + ex.getMessage());
        }
    }
    else if (List.class.equals(collectionType)) {
        return new ArrayList(initialCapacity);
    }
    else if (SortedSet.class.equals(collectionType)) {
        return new TreeSet();
    }
    else {
        return new LinkedHashSet(initialCapacity);
    }
}

因此,作为第一步,从您的上下文定义创建一个Set,然后通过PropertyEditor实现将其转换为List。从同一个包中查看其他PropertyEditors,其中一些执行类似的隐式转换,例如地图&lt; - &gt;属性等。