DI на основі сетера (setter) виконується шляхом виклику контейнером сетерів для бінів після виклику конструктора без аргументів або статичного фабричного методу без аргументів для створення екземпляра біна.

У наступному прикладі показаний клас, залежність до якого може бути впроваджена виключно через сетер. Цей клас є звичайним Java-класом. Це POJO (простий об'єкт мови Java), який не залежить від конкретних інтерфейсів контейнера, базових класів або анотацій.

Java
public class SimpleMovieLister {
    // SimpleMovieLister залежить від MovieFinder
    private MovieFinder movieFinder;
    // Сетер, що дозволяє контейнеру Spring впровадити MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // Бізнес-логіка, яка фактично використовує впроваджений MovieFinder, опущена...
}
Kotlin
class SimpleMovieLister {
    // властивість з відкладеною ініціалізацією, яка дозволяє контейнеру Spring впровадити MovieFinder
    lateinit var movieFinder: MovieFinder
    // Бізнес-логіка, яка фактично використовує впроваджений MovieFinder, опущена...
}

ApplicationContext підтримує DI на основі конструктора та на основі сетера для бінів, якими він керує. Він також підтримує DI на основі сетера після того, як деякі залежності вже запроваджено за допомогою підходу з використанням конструктора. Ти налаштовуєш залежності у вигляді BeanDefinition, які використовуєш разом з екземплярами PropertyEditor для перетворення властивостей з одного формату на інший. Однак більшість користувачів Spring працюють з цими класами не безпосередньо (тобто програмно), а з XML-визначеннями bean, анотованими компонентами (тобто класами, позначеними анотаціями @Component, @Controller тощо) або методами, позначеними анотацією @Bean, у класах, позначених анотацією @Configuration, на основі Java. Ці джерела потім внутрішньо перетворюються на екземпляри BeanDefinition і використовуються для завантаження всього екземпляра IoC-контейнера Spring.

DI на основі конструктора чи сетера?

Оскільки можна змішувати DI на основі конструктора та DI на основі сетера, хорошим практичним правилом є використання конструкторів для обов'язкових залежностей, а сетерів чи методів конфігурації — для необов'язкових залежностей. Зверни увагу: використання анотації на сетері може бути здійснене для того, щоб перетворити властивість на необхідну залежність. Проте краще використовувати впровадження через конструктор із програмною перевіркою аргументів.

Команда Spring загалом підтримує впровадження через конструктор, оскільки це дозволяє реалізувати компоненти програми у вигляді незмінних об'єктів і гарантує, що необхідні залежності не будуть null. Ба більше, компоненти, що впроваджуються через конструктор, завжди повертаються клієнтському (той, що викликається) коду в повністю ініціалізованому стані. Примітка: присутність великої кількості аргументів конструктора — це проблемний код із душком (code smell), який говорить про те, що клас, ймовірно, надто перевантажений і підлягає рефакторингу для кращого розподілу завдань.

Впровадження через сетер слід здебільшого використовувати лише для необов'язкових залежностей, яким можна надати розумні значення за замовчуванням у класі. В іншому випадку перевірка на not-null повинна виконуватися скрізь, де в коді використовується залежність. Однією з переваг впровадження через сетер є й те, що встановлюючі методи роблять об'єкти цього класу придатними до наступної реконфігурації чи повторного застосування. Тому керування через JMX MBeans є адекватним прикладом використання впровадження через сетер.

Використовуй стиль DI, який найбільше підходить для конкретного класу. Іноді, якщо йдеться про інші класи, для яких у тебе немає вихідного коду, вибір робиться за тебе. Наприклад, якщо сторонній клас не відкриває будь-які встановлюючі методи, тоді впровадження через конструктор може бути єдиною доступною формою впровадження залежностей.