我正在阅读“春季食谱”一书,并尝试用例子来研究春天的“魔法”。这就是我所拥有的。 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如何在内部解决这个问题?
答案 0 :(得分:4)
Spring有一大堆方法调用来生成你的上下文。当它解析你的XML上下文时,Spring会生成一个RootBeanDefinition
对象来描述你的bean(类,属性等)和PropertyValues
对象,这些对象包含属性的名称(<property>
)及其对象值。
在这种情况下,它会创建一个ManagedSet
用于保存托管Set值的标记集合类,可以使用 包括运行时bean引用(要解析为bean对象)
保留A
和URL
值。
以上是在任何实际bean对象的字段初始化之前完成的(BeanPostProcessors
做他们的事情,代理等等),尽管实例本身(默认字段值为null
)是创建。您可以通过创建一个空构造函数并在调试时添加断点来看到这一点。
更进一步,在AbstractApplicationContext#refresh()
中,finishBeanFactoryInitialization()
被调用,初始化最终发生。对于之前创建的每个BeanDefinition
和相应的PropertyValues
,Spring会在创建您的bean的applyPropertyValues()
上调用BeanFactory
。
对于suffixes
字段,Spring会发现预期的类型与实际类型不匹配。 TypeConverterDelegate
将确定要使用PropertyEditor
或ConversionService
从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和上下文中声明的每个属性执行相同的逻辑。
答案 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;属性等。