spring
Инъекция зависимостей (DI) и инверсия контроля (IoC)
Поиск…
замечания
Исходный код для больших программных приложений обычно организован в несколько блоков. Определение единицы обычно зависит от используемого языка программирования. Например, код, написанный на языке процедурного программирования (например, C), организован в functions
или procedures
. Аналогично, код на объектно-ориентированном языке программирования (например, Java, Scala и C #) организован в classes
, interfaces
и так далее. Эти единицы организации кода можно рассматривать как отдельные единицы, составляющие общее программное приложение.
Когда приложения имеют несколько блоков, межзависимые отношения между этими единицами возникают, когда одному устройству приходится использовать других для выполнения своих функций. Зависимые единицы могут считаться consumers
и подразделениями, от которых они зависят, в качестве providers
определенных функций.
Самый простой подход к программированию заключается в том, чтобы потребители полностью контролировали поток программного приложения, решая, какие поставщики следует создавать, использовать и уничтожать в каких точках общего выполнения приложения. Говорят, что потребители имеют полный контроль над поставщиками во время потока выполнения, которые являются dependencies
для потребителей. В случае, если у поставщиков есть свои зависимости, потребителям, возможно, придется беспокоиться о том, как провайдеры должны быть инициализированы (и выпущены), что делает поток управления все более и более сложным по мере увеличения количества единиц в программном обеспечении. Этот подход также увеличивает связь между блоками, что делает все более трудным изменять отдельные единицы, не беспокоясь о нарушении других частей программного обеспечения.
Inversion of Control (IoC) - это принцип разработки, который защищает аутсорсинг от потоков управления, таких как обнаружение единиц, создание экземпляров и уничтожение, независимо от потребителей и поставщиков. Основополагающим принципом IoC является развязка потребителей и поставщиков, освобождение программных единиц от необходимости беспокоиться об обнаружении, создании и очистке их зависимостей, а также позволяет подразделениям сосредоточиться на своей собственной функциональности. Эта развязка помогает поддерживать расширяемость программного обеспечения.
Инъекция зависимостей является одним из способов реализации инверсии принципа управления, при котором экземпляры зависимостей (поставщиков) вводятся в программный блок (потребитель) вместо потребителя, который должен найти и создать экземпляр.
Spring Framework содержит модуль ввода зависимостей в своем ядре, который позволяет Spring-управляемым компонентам быть введенными в другие управляемые Spring бобы в качестве зависимостей.
Внедрение зависимости вручную через конфигурацию XML
Рассмотрим следующие классы Java:
class Foo {
private Bar bar;
public void foo() {
bar.baz();
}
}
Как видно, класс Foo
требует вызова метода baz
на экземпляр другого класса Bar
чтобы его метод foo
успешно работал. Bar
как говорят, является зависимостью от Foo
поскольку Foo
не может работать корректно без экземпляра Bar
.
Впрыск конструктора
При использовании конфигурации XML для Spring Framework для определения управляемых Spring бобы, bean типа Foo
можно настроить следующим образом:
<bean class="Foo">
<constructor-arg>
<bean class="Bar" />
</constructor-arg>
</bean>
или, альтернативно (более подробный):
<bean id="bar" class="bar" />
<bean class="Foo">
<constructor-arg ref="bar" />
</bean>
В обоих случаях Spring Framework сначала создает экземпляр Bar
и injects
его в экземпляр Foo
. В этом примере предполагается, что класс Foo
имеет конструктор, который может принимать экземпляр Bar
в качестве параметра, то есть:
class Foo {
private Bar bar;
public Foo(Bar bar) { this.bar = bar; }
}
Этот стиль известен как инъекция конструктора, потому что зависимость (экземпляр Bar
) вводится через конструктор класса.
Впрыск недвижимости
Другой вариант, чтобы ввести зависимость Bar
в Foo
:
<bean class="Foo">
<property name="bar">
<bean class="Bar" />
</property>
</bean>
или, альтернативно (более подробный):
<bean id="bar" class="bar" />
<bean class="Foo">
<property name="bar" ref="bar" />
</bean>
Это требует, чтобы класс Foo
имел метод setter, который принимает экземпляр Bar
, например:
class Foo {
private Bar bar;
public void setBar(Bar bar) { this.bar = bar; }
}
Внедрение зависимостей вручную через конфигурацию Java
Те же примеры, что и выше, с конфигурацией XML, можно переписать с конфигурацией Java следующим образом.
Впрыск конструктора
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() { return new Foo(bar()); }
}
Впрыск недвижимости
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.setBar(bar());
return foo;
}
}
Автоустановка зависимости через конфигурацию XML
Зависимости могут быть автообновлены при использовании функции проверки компонентов платформы Spring. Для работы автоустройства необходимо выполнить следующую конфигурацию XML:
<context:annotation-config/>
<context:component-scan base-package="[base package]"/>
где base-package
- это полностью квалифицированный пакет Java, в котором Spring должен выполнять проверку компонентов.
Впрыск конструктора
Зависимости могут быть введены через конструктор классов следующим образом:
@Component
class Bar { ... }
@Component
class Foo {
private Bar bar;
@Autowired
public Foo(Bar bar) { this.bar = bar; }
}
Здесь @Autowired
представляет собой аннотацию, специфичную для Spring. Spring также поддерживает JSR-299, чтобы обеспечить переносимость приложений к другим основанным на Java платформам инъекций зависимостей. Это позволяет @Autowired
заменяться на @Inject
как:
@Component
class Foo {
private Bar bar;
@Inject
public Foo(Bar bar) { this.bar = bar; }
}
Впрыск недвижимости
Зависимости также можно вводить с использованием методов сеттера следующим образом:
@Component
class Foo {
private Bar bar;
@Autowired
public void setBar(Bar bar) { this.bar = bar; }
}
Полевая инъекция
Autowiring также позволяет инициализировать поля внутри экземпляров класса напрямую, а именно:
@Component
class Foo {
@Autowired
private Bar bar;
}
Для весенних версий 4.1+ можно использовать Дополнительно для дополнительных зависимостей.
@Component
class Foo {
@Autowired
private Optional<Bar> bar;
}
Тот же подход может быть использован для конструктора DI.
@Component
class Foo {
private Optional<Bar> bar;
@Autowired
Foo(Optional<Bar> bar) {
this.bar = bar;
}
}
Автоподключение зависимости через конфигурацию Java
Встраивание конструктора через конфигурацию Java также может использовать автоподключение, например:
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo(Bar bar) { return new Foo(bar); }
}