2013-08-23 3 views
3

Я недавно начал работать над весной 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"); 
    } 
} 

Ниже приведен пример конфигурационного файла весны, который я использовал.

<?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> 

В приведенном выше случае конструктор, вызванный пружиной, является вторым конструктором. Я не понимаю, почему весна решила вызывать второй конструктор вместо первого? В таких случаях, как описано выше, как весна решает, какой конструктор вызывается при передаче атрибута типа?

ответ

6

Spring использует экземпляр ConstructorResolver, чтобы решить, какой конструктор использовать для создания экземпляра вашего класса. Он вызывает метод autowireConstructor(), чтобы определить это. Вы можете найти исходный код онлайн. Старая версия, here. Если у вас есть источник (используйте maven), вы можете отлаживать и проходить через него самостоятельно.

Внутри метода он пытается определить разницу между указанными аргументами и аргументами в контроллере с помощью метода ArgumentsHolder#getTypeDifferenceWeight(). В нашем случае он вернет значение 0, потому что аргументы совпадают (даже в другом порядке).

Это значение сравнивается с величиной minTypeDiffWeight (первоначально Integer.MAX_VALUE). Если он меньше, текущий конструктор, который оценивается, получает приоритет, а значение заменяет minTypeDiffWeight. Метод продолжается так же через все конструкторы класса, сравнивая снова vs 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

Спасибо за подробное объяснение Sotirios. Выполняется ли вышеуказанный процесс в первом сценарии. Если выполняется тот же процесс, то почему он вызвал первый конструктор в первом сценарии? – Beast

+1

@Beast Процесс тот же, но здесь порядок параметров имеет значение. «ConstructorResolver» просматривает массив конструкторов, пытается использовать '(int, String)', но терпит неудачу, потому что значение 'Jinesh' не может быть преобразовано в' int'. Возникает 'UnsatisfiedDependencyException' и этот конструктор пропускается. Второй конструктор в массиве становится кандидатом (первым в вашем примере) и потому, что '' 77 "' можно преобразовать в 'int', он выбирается. Где-то в стеке, есть система преобразования, делающая вещи. –

+0

Благодарим за вашу помощь и всю вашу помощь. Один последний вопрос, если я хочу отлаживать исходный код весны через maven, есть ли у вас какие-либо файлы конфигурации? – Beast

1

Вы можете явно указать порядок аргументов конструктора, добавив атрибут индекса.

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

Я предполагаю, что вы можете включать в индекс и тип, хотя spring reference docs не явно говорит, что вы можете.

С весной 3 вы можете указать имя параметра, на который вы ссылаетесь, - чтобы удалить всю двусмысленность - поэтому, если вы не можете использовать тип и индекс вместе, это ваше решение.

+2

Сначала у меня были сомнения относительно имени параметра, потому что это невозможно с отражением. Я пошел и заглянул в него, и Весна немного охлаждает магию с анализом байтового кода, чтобы получить имена параметров. –

Смежные вопросы