1

我正在阅读“Spring Recipes”一书,并尝试通过示例来研究 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配置:

<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 中,我有意使用标签来观察结果。我也写了两次相同的值“A”。我注意到 Spring 为“后缀”属性注入了 ArrayList,作为 bean 类中的类型定义。但它的行为类似于输出设置,仅包含一个“A”值。有人知道Spring如何在内部解决这个问题吗?

4

2 回答 2

4

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

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

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

保持你的A价值观URL

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

再往下, in AbstractApplicationContext#refresh()finishBeanFactoryInitialization()在初始化最终发生的地方被调用。对于BeanDefinition之前创建的每个和相应的PropertyValues,Spring 调用创建您applyPropertyValues()BeanFactorybean。

对于您的suffixes领域,Spring 发现预期类型与实际类型不匹配。ATypeConverterDelegate将确定使用哪个PropertyEditorConversionService用于从PropertyValues(the ManagedSet) 转换为实际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. 然后使用 aBeanWrapperImpl设置属性值。为此,它Method通过反射找到您的设置器并使用ArrayList.

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

于 2013-09-04T21:08:29.773 回答
0

所有的魔法都发生在 org.springframework.beans.propertyeditors.CustomCollectionEditor 中。它负责从上下文提供的对象(在您的示例中为 Set)创建某种类型的属性(在您的情况下为 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。查看同一包中的其他 PropertyEditor,其中一些执行类似的隐式转换,例如 Map <-> Properties 等。

于 2013-09-04T21:03:51.850 回答