Несколько дней новогоднего волшебства:
Успейте начать обучение в 2018-ом году со скидкой до 30%!
Выбрать курс

Варианты DI в Spring

Dependency Injection в Spring можно осуществлять различными способами. Рассмотрим плюсы и минусы данных подходов.

Например, рассмотрим DI через параметры конструктора

@Service
public class MyService {
    private Dependency dependency;
    @Autowired
    public MyService(Dependency dependency) {
        this.dependency = dependency;
    }
}
Данный подход является рекомендованным. Разберёмся с некоторыми ситуациями, где DI через параметры конструктора может выглядеть не самым лучшим образом, и выясним, причины этого:

Чуешь, чем пахнет?
Появляется большое число параметров конструктора, что является «запахом плохого кода». Если для создания бина требуется множество зависимостей, то DI через конструктор, как ни странно, ни при чём. Если в классе большое число зависимостей, то это говорит, скорее о неправильном дизайне самого сервиса.

Как пони по кругу
Если между классами есть циклическая зависимость, то DI через конструктор не получится организовать. Действительно, это так, но если появилась циклическая зависимость между классами, то это, скорее, неправильное разделение ответственности между сервисами.

У DI через параметры конструктора, есть безусловные плюсы:
  • в любой момент времени вы всегда получите готовый к работе класс со всеми зависимостями;
  • данные классы можно просто тестировать как обычные Java-классы и без поднятия контекста для тестирования;
  • в unit-тестах вы не сможете забыть добавить какую-либо зависимость, так как она будет требоваться на уровне компиляции.

  • С циклическими зависимостями можно, в частности, бороться с помощью

    DI через поля:

    @Service
    public class MyService {
        @Autowired
        private Dependency dependency;
    }
    
    Несмотря на действительно минимальное количество кода, этот подход обладает одним большим недостатком. Данный класс нельзя просто протестировать, не поднимая Spring Context. Если для контроллеров и других классов в любом случае придётся его поднимать, то для бизнес-сервисов это излишне.

    Ну и DI через сеттеры

    @Service
    public class MyService {
        private Dependency dependency;
        @Autowired
        public void setDependency(Dpendency dependency) {
            this.dependency = dependency;
        }
    }
    
    В данном случае тестировать эти классы почти так же просто, как и в случае DI через параметры конструктора. Но в тестах очень легко забыть проставить зависимость, не вызвав свежедобавленный сеттер.

    Этот способ обладает одним серьёзным недостатком: до того момента, как Spring проставит все зависимости в бин, экземпляр класса находится в некорректном состоянии. И обращение к зависимостям может привести к NullPointerException. Для того, чтобы сделать какие-то действия после того, как все зависимости будут инъектированы, используют аннотацию @PostConstruct:

    @Service
    public class MyService {
        private Dependency dependency;

    @Autowired public void setDependency(Dpendency dependency) { this.dependency = dependency; }

    @PostConstruct public void init() { this.dependency.doSomething(); } }

    Остались вопросы? Задавайте в комментариях!

    Автор
    0 комментариев
    Для комментирования необходимо авторизоваться