java - beans xml 配置中带有 <set> 标记的列表的行为与 Set 相同

标签 java spring

我正在阅读《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 为“后缀”属性注入(inject)了 ArrayList,作为它在 bean 类中的类型定义。但它的行为就像在输出上设置一样,仅包含一个“A”值。有人知道 Spring 内部是如何解决这个问题的吗?

最佳答案

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

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

Tag collection class used to hold managed Set values, which may include runtime bean references (to be resolved into bean objects)

保存你的AURL值。

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

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

为了您的suffixes字段中,Spring 发现预期类型与实际类型不匹配。一个TypeConverterDelegate会找出哪个PropertyEditorConversionService用于从 PropertyValues 进行转换( 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 。使用BeanWrapperImpl ,然后设置属性值。为此,它会找到您的 setter Method通过反射并使用 ArrayList 调用它.

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

关于java - beans xml 配置中带有 <set> 标记的列表的行为与 Set 相同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18623268/

相关文章:

java - j8583 库 - 如何将值转换为十六进制

java - "throws Exception"的用途

java - Spring @Service 与对象服务

java - 如何在 JDBC 模板中使用 UUID?

java - Spring内部-成员实例注入(inject)澄清

java - 在 WebSphere Liberty 上工作时的 Spring mvc 多文件上传

java - 如何在 Spring Boot 中从 UI 服务消费登录服务?

java - 从 JSON 转换为 Map 的递归函数

java - 如何将输入数据从 private void GUI 传输到 java 类中的 ArrayList?

java - 为什么 JRuby 在重载时不能使用我的参数是 RubyArray 的 Java 方法?