Подтвердить что ты не робот

Как правильно выполнить инъекцию зависимостей (в Spring)?

У меня есть сомнения по поводу ввода объектов в класс с помощью Spring. Я использовал в своих проектах такой код:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

то он обычно используется в методах как:

personRepository.save(p);

В противном случае я нашел в примерах Spring, введя конструктор:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

Итак, оба правильны? Или каждый из них имеет свои свойства и обычаи?

4b9b3361

Ответ 1

tl; dr - Инъекция конструктора - лучший способ сделать DI

Последнее верно, и это связано не столько с Spring, либо с любым контейнером инъекций зависимостей, но с принципами объектно-ориентированного класса.

Подробнее

Тип должен быть спроектирован так, чтобы вы могли создавать только экземпляры из него, которые находятся в допустимом состоянии. Для этого все обязательные зависимости этого типа должны быть аргументами конструктора. Это означает, что эти зависимости могут быть null -checked, назначены конечным полям для обеспечения неизменности. Кроме того, при работе с кодом для вызывающего (или создателя) этого экземпляра сразу становится очевидным, какие зависимости он должен предоставить (путем сглаживания через документы API или с использованием завершения кода в среде IDE).

Все это невозможно при инъекции поля. Вы не видите зависимости извне, вам требуется черная магия для встраивания зависимостей, и вы никогда не можете быть уверены, что они не null, за исключением того, что вы слепо доверяете контейнеру.

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

Контейнеры

Люди часто утверждают, что это просто академическое дерьмо, так как вы можете положиться на контейнер в любом случае. Здесь я беру на себя следующее:

  • Просто потому, что контейнер существует, это не значит, что вы должны бросить все основные объектно-ориентированные принципы дизайна на борту, не так ли? Вы все еще принимаете душ, даже если существуют антиперспиранты, правильно?

  • Даже типы, предназначенные для использования с контейнером, будут использоваться вручную: в модульных тестах. Если вы не пишете модульные тесты... ну, тогда другая тема.

  • Предполагаемая многословность дополнительного конструктора ( "Я могу добиться одного и того же элемента с одной линией ввода в поле!" - "Нет, вы не можете. На самом деле вы получаете материал от написания строки кода больше." ) могут быть смягчены такими вещами, как Lombok. Компонент с инжектором конструктора с Spring и Lombok выглядит следующим образом:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok позаботится о создании конструктора, берущего параметр для каждого конечного поля, и проверит заданный параметр для null перед его назначением. Таким образом, вы эффективно получаете краткость впрыска поля и конструктивные преимущества впрыска конструктора.

Эпилог

Недавно я был участником дискуссии с некоторыми людьми, не относящимися к Java, которые я ужасно озадачил, используя термин "инъекция" для конструктора DI. Фактически, они утверждали - и в этом есть много правды, - что передача зависимостей через конструктор вовсе не является инъекцией, так как это самый естественный способ передать объекты другим (резко контрастирует с инъекциями любых видов).

Может быть, мы должны использовать другой термин для такого стиля? Зависимость кормления, может быть?

Ресурсы

Ответ 2

В академическом плане я согласен, что конструкторы - это просто лучший способ создания объектов. Однако спецификация java beans построена на предложении мутаторов, чтобы облегчить отражение. Слишком много инструментов и фреймворков построено вокруг этой легкости доступа к парадигме. Для служб, DAO и других синглтонных сценариев я считаю, что только инъекция конструктора должна использоваться, поскольку мутаторы нарушают старое правило "только друзья могут видеть ваши частные части".