2013-08-23 87 views
3

我最近开始在spring 3.2上工作。我试图理解构造函数参数解析的情况下,何时通过构造函数注入传递依赖关系。我创建了下面的例子。构造函数参数解析

package com.springinaction.springidol; 

public interface Performer { 
    void perform(); 
} 
package com.springinaction.springidol; 

public class Juggler implements Performer { 

    private int beanBags=3; 
    private String name; 

    public Juggler(){ 
    } 

    public Juggler(String name,int beanBags){ 
     System.out.println("First constructor gets called"); 
     this.beanBags=beanBags; 
     this.name=name; 
    } 

    public Juggler(int beanBags,String name){ 
     System.out.println("Second constructor gets called"); 
     this.beanBags=beanBags; 
     this.name=name; 
    } 

    public void perform(){ 
    System.out.println("JUGGLING "+beanBags+name+" BEANBAGS"); 
    } 
} 

请查看下面我用过的spring配置文件的实例。

<?xml version="1.0" encoding="UTF-8" ?> 

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 

<bean id="duke" class="com.springinaction.springidol.Juggler"> 
    <constructor-arg value="Jinesh" /> 
    <constructor-arg value="77" /> 
</bean> 

在上述情况下调用的构造是第一构造函数。但在此之后,我稍微更改了xml文件并为这两个参数添加了type属性。

<?xml version="1.0" encoding="UTF-8" ?> 

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 

<bean id="duke" class="com.springinaction.springidol.Juggler"> 

<constructor-arg type="java.lang.String" value="Jinesh" /> 
<constructor-arg type="int" value="77" /> 

</bean> 

</beans> 

在上面的例子中,spring调用的构造函数是第二个构造函数。我不明白为什么春天决定调用第二个构造函数而不是第一个构造函数?在上面的例子中,spring如何决定在传递type属性时调用哪个构造函数?

回答

6

Spring使用ConstructorResolver实例来解析用于实例化类的构造函数。它调用autowireConstructor()方法来确定。您可以在线找到源代码。旧版本,here。如果你有源代码(使用maven),你可以自己调试和浏览它。

在该方法中,它尝试使用方法ArgumentsHolder#getTypeDifferenceWeight()来确定指定参数与控制器中参数之间的差异。在我们的例子中,它会返回值0,因为参数匹配(即使按不同的顺序)。

该值与minTypeDiffWeight值(最初为Integer.MAX_VALUE)进行比较。如果它更小,则当前正在评估的构造函数将获得优先级,并且该值将替代minTypeDiffWeight。该方法继续像所有Class的构造函数一样,再次与minTypeDiffWeight进行比较。由于两个构造函数的值都是0(0不小于0),所以使用第一个构造函数。

碰巧的是

Juggler.class.getDeclaredConstructors(); 

返回类似

[public Test.Juggler(int,java.lang.String), public Test.Juggler(java.lang.String,int), public Test.Juggler()] 

其中第二(声明)的构造首先出现的阵列。该方法getDeclaredConstructors()的Javadoc指出

返回不排序的数组中的元素,并且不以任何 特定顺序。

所以这只是巧合。因为参数类型匹配,所以Spring选择它在该数组中找到的第一个构造函数。

+0

感谢这样详细的解释索蒂里奥斯。第一种情况下是否也遵循上述流程?如果遵循相同的流程,那么为什么它在第一个场景中调用了第一个构造函数? – Beast

+1

@Beast过程是一样的,但这里参数的顺序很重要。在'ConstructorResolver'遍历构造的阵列中,尝试使用'(INT,字符串)'但失败了,因为值'Jinesh'不能转换到一个'int'。发生'UnsatisfiedDependencyException'并且该构造函数被跳过。所述阵列中的第二个构造变成候选(第一在您的示例),并且因为'“77”'可以被转换为一个'int',它被选择。在堆栈的某个地方,有一个转换系统在做某些事情。 –

+0

感谢您的replyour的帮助,如果我想通过基于Maven春源代码你们有任何配置文件来调试所有help.One最后一个问题? – Beast

1

您可以通过添加索引属性来显式指定构造函数参数的排序。

<constructor-arg type="java.lang.String" index="0" value="Jinesh" /> 
<constructor-arg type="int" index="1" value="77" /> 

我想你可以包括指数虽然spring reference docs没有明确说你可以键入在一起。

使用Spring 3实际上你可以指定你指的是参数的名称 - 删除所有不确定性 - 如果你不能使用类型和索引在一起,这是你的解决方案。

+2

起初,我有我的有关参数名的事情的疑惑,因为这是不可能与反思。我去看了看,Spring用字节代码分析来做一些很酷的魔法来检索参数的名字。 –