DI на основе конструктора выполняется путем вызова контейнером конструктора с рядом аргументов, каждый из которых представляет зависимость. Вызов статического
фабричного метода с определенными аргументами для создания бина почти эквивалентен, и в данном анализе аргументы конструктора и статического
фабричного метода рассматриваются одинаково. В следующем примере показан класс, зависимость в который может быть внедрена исключительно через конструктор:
public class SimpleMovieLister {
// SimpleMovieLister имеет зависимость от MovieFinder
private final MovieFinder movieFinder;
// конструктор для того, чтобы контейнер Spring мог внедрить MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// бизнес-логика, которая фактически использует внедренный MovieFinder, опущена...
}
// конструктор для того, чтобы контейнер Spring мог внедрить MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
// бизнес-логика, которая фактически использует внедренный MovieFinder, опущена...
}
Обратите внимание, что в данном классе нет ничего особенного. Это POJO (простой объект языка Java), который не зависит от конкретных интерфейсов контейнера, базовых классов или аннотаций.
Разрешение аргументов конструктора
Сопоставление разрешения аргумента конструктора происходит с использованием типа аргумента. Если в аргументах конструктора определения бина не существует потенциальной неоднозначности, то порядок, в котором аргументы конструктора определены в определении бина, является порядком, в котором эти аргументы передаются соответствующему конструктору при создании бина. Рассмотрим следующий класс:
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
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 не может определить тип значения, и поэтому не может выполнить сопоставление по типу без сторонней помощи. Рассмотрим следующий класс:
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;
}
}
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>
Помимо устранения неоднозначности нескольких простых значений, задание индекса устраняет неоднозначность, если конструктор имеет два аргумента одного типа.
Вы также можете использовать имя параметра конструктора для устранения противоречий значений, как показано в следующем примере:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
Имейте в виду, что для того, чтобы это работало "из коробки", код должен быть скомпилирован с включенным флагом отладки (debug), чтобы Spring мог найти имя параметра в конструкторе. Если вы не можете или не хотите компилировать свой код с флагом отладки, то можно использовать аннотацию @ConstructorProperties JDK для явного именования аргументов вашего конструктора. Тогда образец класса должен будет выглядеть следующим образом:
package examples;
public class ExampleBean {
// Поля опущены
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
package examples
class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ