Я уже некоторое время использую Dependency Injection в Spring, и я понимаю, как это работает и каковы некоторые плюсы и минусы его использования. Однако, когда я создаю новый класс, я часто задаюсь вопросом - должен ли этот класс управляться Spring IOC Container?
И я не хочу говорить о различиях между аннотацией @Autowired, конфигурацией XML, внедрением сеттера, внедрением конструктора и т. Д. У меня общий вопрос.
Допустим, у нас есть сервис с конвертером:
@Service
public class Service {
@Autowired
private Repository repository;
@Autowired
private Converter converter;
public List<CarDto> getAllCars() {
List<Car> cars = repository.findAll();
return converter.mapToDto(cars);
}
}
@Component
public class Converter {
public CarDto mapToDto(List<Car> cars) {
return new ArrayList<CarDto>(); // do the mapping here
}
}
Очевидно, что конвертер не имеет каких-либо зависимостей, поэтому для него не требуется автоматическое подключение. Но для меня это кажется лучше, как с автопроводкой. Код чище и его легко проверить. Если я напишу этот код без DI, сервис будет выглядеть так:
@Service
public class Service {
@Autowired
private Repository repository;
public List<CarDto> getAllCars() {
List<Car> cars = repository.findAll();
Converter converter = new Converter();
return converter.mapToDto(cars);
}
}
Сейчас это гораздо сложнее проверить. Более того, новый конвертер будет создаваться для каждой операции преобразования, даже если он всегда находится в одном и том же состоянии, что выглядит как накладные расходы.
В Spring MVC есть несколько хорошо известных шаблонов: контроллеры, использующие сервисы, и сервисы, использующие репозитории. Затем, если Репозиторий имеет автоматическое подключение (что обычно и происходит), то Служба также должна быть подключена автоматически. И это вполне понятно. Но когда мы используем аннотацию @Component? Если у вас есть статические классы утилит (например, конвертеры, картографы) - вы их автоматически связываете?
Вы пытаетесь сделать все классы автоматически подключенными? Тогда все зависимости класса легко внедрить (еще раз, легко понять и легко проверить). Или вы пробуете автопровод только тогда, когда это абсолютно необходимо?
Я потратил некоторое время на поиски некоторых общих правил о том, когда использовать автопроводку, но не смог найти никаких конкретных советов. Обычно люди говорят о том, «используете ли вы DI? (Да / нет)» или «какой тип внедрения зависимостей вы предпочитаете», что не отвечает на мой вопрос.
Буду благодарен за любые советы по этой теме!
источник
Ответы:
Согласитесь с комментариями @ ericW и просто хотите добавить, что вы можете использовать инициализаторы для сохранения компактности своего кода:
или
или, если класс действительно не имеет состояния вообще
Один из ключевых критериев того, должен ли Spring создавать экземпляр и внедрять bean-компонент, заключается в том, является ли этот bean-компонент настолько сложным, что вы хотите высмеять его при тестировании? Если это так, то введите его. Например, если конвертер совершает обратную передачу к какой-либо внешней системе, вместо этого сделайте его компонентом. Или, если возвращаемое значение имеет большое дерево решений с десятками возможных вариаций, основанных на вводе, то имитируйте его. И так далее.
Вы уже проделали хорошую работу по свертыванию этой функциональности и ее инкапсуляции, поэтому теперь остается только вопрос, достаточно ли она сложна, чтобы ее можно было рассматривать как отдельный «модуль» для тестирования.
источник
Я не думаю, что вам нужно @Autowired для всех ваших классов, это должно зависеть от реального использования, для ваших сценариев лучше использовать статический метод вместо @Autowired. Я не вижу никакого преимущества в использовании @Autowired для этих простых утилит, и это абсолютно увеличит стоимость контейнера Spring, если его не использовать должным образом.
источник
Мое эмпирическое правило основано на том, что вы уже сказали: тестируемость. Задайте себе вопрос: « Могу ли я легко протестировать его? » Если ответ «да», то при отсутствии какой-либо другой причины я был бы согласен с этим. Таким образом, если вы разрабатываете юнит-тест в то же время, что и вы, вы сэкономите много боли.
Единственная потенциальная проблема заключается в том, что в случае сбоя преобразователя ваш сервисный тест также не пройдет. Некоторые люди скажут, что вы должны высмеивать результаты конвертера в модульных тестах. Таким образом, вы сможете быстрее выявить ошибки. Но у него есть цена: вы должны смоделировать все результаты преобразователей, когда настоящий преобразователь мог бы сделать работу.
Я также предполагаю, что нет никакой причины использовать разные dto конвертеры.
источник
TL; DR: гибридный подход автоматического подключения для DI и перенаправления конструктора для DI может упростить код, который вы представили.
Я смотрел на подобные вопросы из-за некоторых проблем с веб-журналом, связанных с ошибками / осложнениями при запуске Spring Framework, связанных с инициализацией бина @autowired. Я начал смешивать в другом подходе DI: пересылка конструктора. Это требует таких условий, как вы представляете («Понятно, что конвертер не имеет никаких зависимостей, поэтому для него не требуется автоматическое подключение».). Тем не менее, мне очень нравится гибкость, и она все еще довольно прямолинейна.
или даже как отрывок от ответа Роба
Это может не потребовать изменения публичного интерфейса, но я бы. Все еще
можно сделать защищенным или закрытым для охвата только для целей тестирования / расширения.
Ключевым моментом является то, что DI не является обязательным. Предусмотрено значение по умолчанию, которое может быть переопределено прямым способом. Он имеет слабость по мере увеличения количества полей, но для 1, может быть, 2 полей я бы предпочел это при следующих условиях:
Последний пункт (немного OT, но связанный с тем, как мы решаем, что / где @autowired): класс конвертера, как он представлен, является служебным методом (никакие поля, никакой конструктор, не может быть статическим). Может быть, решение будет иметь какой-то метод mapToDto () в классе Cars? То есть подтолкнуть инъекцию преобразования к определению Cars, где, вероятно, уже тесно связаны:
источник
Я думаю, что хорошим примером этого является что-то вроде SecurityUtils или UserUtils. В любом приложении Spring, которое управляет пользователями, вам понадобится класс Util с целым набором статических методов, таких как:
getCurrentUser ()
isAuthenticated ()
isCurrentUserInRole (Полномочия органа)
и т.п.
и я никогда не проводил их автоматически JHipster (который я использую в качестве довольно хорошего судьи передовой практики) не делает.
источник
Мы можем разделить классы на основе функции этого класса таким образом, что контроллеры, сервис, репозиторий, сущность. Мы можем использовать другие классы для нашей логики только не для целей контроллеров, службы и т. Д., В этом случае мы могли бы аннотировать эти классы с помощью @component. Это автоматически зарегистрирует этот класс в контейнере Spring. Если он зарегистрирован, то он будет управляться контейнером Spring. Концепция внедрения зависимостей и инверсии управления может быть предоставлена этому аннотированному классу.
источник