《Spring攻略(第2版)》——1.4 解决构造程序歧义
本节书摘来自异步社区《Spring攻略(第2版)》一书中的第1章,第1.4节,作者: 【美】Gary Mak , Josh Long , Daniel Rubio着,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.4 解决构造程序歧义
1.4.1 问题
当你为Bean指定一个或者多个构造程序参数时,Spring将试图在Bean类中查找对应的构造程序,并且传递用于Bean实例化的参数。但是,如果你的参数可以应用到超过一个构造程序,可能在构造程序匹配中造成歧义。在这种情况下,Spring可能无法调用你所预期的构造程序。
1.4.2 解决方案
你可以为元素指定type和index属性,帮助Spring查找预期的构造程序。
1.4.3 工作原理
现在添加一个新的构造程序到SequenceGenerator类,参数为prefix和suffix。
package com.apress.springrecipes.sequence;
public class SequenceGenerator {
...
public SequenceGenerator(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
}
在Bean声明中,你可以通过元素指定一个或者多个构造程序参数。Spring将尝试为该类寻找合适的构造程序,并传递用于Bean实例化的参数。回忆一下,中没有name属性,因为构造程序参数是基于位置的。
<bean
>
<constructor-arg value="30" />
<constructor-arg value="A" />
<property name="initial" value="100000" />
</bean>
Spring很容易为这两个参数找到一个构造程序,因为只有一个构造程序需要两个参数。假定你必须为SequenceGenerator添加另一个具有prefix和initial参数的构造程序。
package com.apress.springrecipes.sequence;
public class SequenceGenerator {
...
public SequenceGenerator(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
public SequenceGenerator(String prefix, int initial) {
this.prefix = prefix;
this.initial = initial;
}
}
为了调用这个构造程序,你建立下面的Bean声明,传递一个前缀和一个初始值。剩下的后缀通过设值方法注入。
<bean
>
<constructor-arg value="30" />
<constructor-arg value="100000" />
<property name="suffix" value="A" />
</bean>
但是,如果你现在运行应用程序,将会得到如下结果:
300A
301A
这个意外结果的起因是调用了第一个带有prefix和suffix参数的构造程序,而不是第二个。这是因为Spring默认将两个参数都解析为String类型,第一个构造程序不需要类型转换,所以被认定为最合适的。为了指定参数的预期类型,你必须设置中的type属性。
<bean
>
<constructor-arg type="java.lang.String" value="30" />
<constructor-arg type="int" value="100000" />
<property name="suffix" value="A" />
</bean>
现在,再为SequenceGenerator添加一个带有initial和suffix参数的构造程序,并相应修改Bean声明。
package com.apress.springrecipes.sequence;
public class SequenceGenerator {
...
public SequenceGenerator(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
public SequenceGenerator(String prefix, int initial) {
this.prefix = prefix;
this.initial = initial;
}
public SequenceGenerator(int initial, String suffix) {
this.initial = initial;
this.suffix = suffix;
}
}
<bean
>
<constructor-arg type="int" value="100000" />
<constructor-arg type="java.lang.String" value="A" />
<property name="prefix" value="30" />
</bean>
如果你再次运行应用程序,可能得到正确的结果,或者得到下列意外的结果:
30100000null
30100001null
这种不确定性的起因是Spring在内部对每个构造程序与参数的兼容性评分。但是在这个评分过程中,没有考虑参数出现在XML中的顺序。这意味着从Spring的角度看,第二个和第三个构造程序将得到同样的分数。选择哪一个构造程序取决于匹配的顺序。根据Java Reflection API,更精确地说是Class.getDeclaredConstructors()方法,返回的构造程序将是任意顺序的,可能与声明的顺序不同。所有这些因素综合起来,导致了构造程序匹配中的歧义。
为了避免这个问题,你必须通过的index属性明确指出参数的索引值。设置了type和index属性,Spring就能够准确地为Bean找到预期的构造程序。
<bean
>
<constructor-arg type="int" index="0" value="100000" />
<constructor-arg type="java.lang.String" index="1" value="A" />
<property name="prefix" value="30" />
</bean>
但是,如果你相当确定构造程序不会导致歧义,就可以忽略type和index属性。
最后更新:2017-05-31 15:01:40