Почему в сервисах и контроллерах не рекомендуется использовать Lombok @RequiredArgsConstructor, а в дата объектах можно
Что проверяют
Понимаете ли вы, что именно генерирует Lombok, и отличаете ли удобство записи от скрытого контракта класса. Вопрос про границу: где сгенерированный код экономит время, а где прячет проектную проблему.
Как отвечать
@RequiredArgsConstructor собирает конструктор из всех полей final и @NonNull в порядке их объявления. Spring при единственном конструкторе внедряет зависимости без @Autowired, поэтому связка и стала популярной.
Почему в сервисах и контроллерах к ней относятся осторожно:
- конструктора не видно в исходнике, состав и порядок параметров меняются молча, когда кто-то добавляет поле;
- на параметр нельзя повесить
@Qualifier,@Valueили@Lazy: аннотация остаётся на поле и не переносится, пока вlombok.configне прописанlombok.copyableAnnotations; - растущий список зависимостей перестаёт бросаться в глаза, а это главный сигнал, что сервис взял на себя лишнее;
- если две зависимости одного типа, перестановка полей меняет смысл вызова там, где конструктор вызывают позиционно, и компилятор молчит.
В дата-объектах этих рисков нет: они не участвуют в разрешении бинов, набор полей и есть их контракт, а конструктор читается по списку полей сверху вниз.
Где спотыкаются
Говорят, что Lombok ухудшает тестируемость, потому что жёстко связывает компоненты. Всё наоборот: конструкторная инъекция это самый удобный для тестов способ, объект собирается через new без поднятия контекста. Проблема не в инъекции, а в невидимости сигнатуры.
Предлагают вместо этого @Autowired на поле. Полевая инъекция прячет зависимость ещё сильнее, не даёт объявить поле final и требует рефлексии в тестах, то есть решает проблему ровно наоборот.
Переносят «в дата-объектах можно» на JPA-сущности вместе с @Data. Сгенерированные equals и hashCode по всем полям ломают работу с коллекциями до сохранения, а toString дёргает ленивые связи и даёт лишние запросы или падение вне сессии.