DI на основе конструктора выполняется путем вызова контейнером конструктора с рядом аргументов, каждый из которых представляет зависимость. Вызов статического фабричного метода с определенными аргументами для создания бина почти эквивалентен, и в данном анализе аргументы конструктора и статического фабричного метода рассматриваются одинаково. В следующем примере показан класс, зависимость в который может быть внедрена исключительно через конструктор:

Java
public class SimpleMovieLister {
    // SimpleMovieLister имеет зависимость от MovieFinder
    private final MovieFinder movieFinder;
    // конструктор для того, чтобы контейнер Spring мог внедрить MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // бизнес-логика, которая фактически использует внедренный MovieFinder, опущена...
}
Kotlin
// конструктор для того, чтобы контейнер Spring мог внедрить MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
    // бизнес-логика, которая фактически использует внедренный MovieFinder, опущена...
}

Обратите внимание, что в данном классе нет ничего особенного. Это POJO (простой объект языка Java), который не зависит от конкретных интерфейсов контейнера, базовых классов или аннотаций.

Разрешение аргументов конструктора

Сопоставление разрешения аргумента конструктора происходит с использованием типа аргумента. Если в аргументах конструктора определения бина не существует потенциальной неоднозначности, то порядок, в котором аргументы конструктора определены в определении бина, является порядком, в котором эти аргументы передаются соответствующему конструктору при создании бина. Рассмотрим следующий класс:

Java
package x.y;
public class ThingOne {
    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}
Kotlin
package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)

Если предположить, что классы ThingTwo и ThingThree не связаны наследованием, то потенциальной двусмысленности не возникает. Таким образом, следующая конфигурация работает нормально, и не нужно четко указывать индексы или типы аргументов конструктора в элементе <constructor-arg/>.

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>
    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

Если ссылка предоставляется на другой бин, то тип известен, поэтому может произойти сопоставление (как это было в предыдущем примере). Если используется простой тип, например <value>true</value>, Spring не может определить тип значения, и поэтому не может выполнить сопоставление по типу без сторонней помощи. Рассмотрим следующий класс:

Java
package examples;
public class ExampleBean {
    // Количество лет для расчета окончательного ответа
    private final int years;
    // Ответ на главный вопрос жизни, вселенной и всего такого
    private final String ultimateAnswer;
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
Kotlin
package examples
class ExampleBean(
    private val years: Int, // Количество лет для расчета окончательного ответа
    private val ultimateAnswer: String // Ответ на главный вопрос жизни, вселенной и всего такого
)
Сопоставление типов аргументов конструктора

В предыдущем сценарии контейнер может задействовать сопоставление типов с простыми типами, если тип аргумента конструктора явно задан с помощью атрибута type, как показано в следующем примере:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
Индекс аргумента конструктора

Вы можете использовать атрибут index, чтобы явно задать индекс аргументов конструктора, как показано в следующем примере:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

Помимо устранения неоднозначности нескольких простых значений, задание индекса устраняет неоднозначность, если конструктор имеет два аргумента одного типа.

Индекс основан на 0 (начинается с 0).
Имя аргумента конструктора

Вы также можете использовать имя параметра конструктора для устранения противоречий значений, как показано в следующем примере:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

Имейте в виду, что для того, чтобы это работало "из коробки", код должен быть скомпилирован с включенным флагом отладки (debug), чтобы Spring мог найти имя параметра в конструкторе. Если вы не можете или не хотите компилировать свой код с флагом отладки, то можно использовать аннотацию @ConstructorProperties JDK для явного именования аргументов вашего конструктора. Тогда образец класса должен будет выглядеть следующим образом:

Java
package examples;
public class ExampleBean {
    // Поля опущены
    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
Kotlin
package examples
class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)