1. Введение
Lombok — чрезвычайно полезная библиотека для преодоления шаблонного кода. Если вы еще не знакомы с ним, настоятельно рекомендую взглянуть на предыдущий туториал — Introduction to Project Lombok .
В этой статье мы продемонстрируем его удобство использования в сочетании с инъекцией зависимостей на основе конструктора
Spring .
2. Внедрение зависимостей на основе конструктора
Хороший способ связать зависимости в Spring с помощью Dependency Injection на основе конструктора
. Этот подход заставляет нас явно передавать зависимости компонента конструктору.
В отличие от Field-Based Dependency Injection
, он также дает ряд преимуществ:
- нет необходимости создавать компонент конфигурации для конкретного теста — зависимости вводятся явно в конструкторе
- последовательный дизайн — все необходимые зависимости подчеркнуты и контролируются определением конструктора
- простые модульные тесты — снижение накладных расходов Spring Framework
- восстановленная свобода использования
конечных
ключевых слов
Однако из-за необходимости написания конструктора это приводит к значительному увеличению базы кода. Рассмотрим два примера GreetingService
и FarewellService:
@Component
public class GreetingService {
@Autowired
private Translator translator;
public String produce() {
return translator.translate("hello");
}
}
@Component
public class FarewellService {
private final Translator translator;
public FarewellService(Translator translator) {
this.translator = translator;
}
public String produce() {
return translator.translate("bye");
}
}
По сути, оба компонента делают одно и то же — вызывают настраиваемый транслятор
с помощью слова, специфичного для задачи.
Однако второй вариант гораздо более запутан из-за шаблонного кода конструктора, который на самом деле не приносит никакой пользы коду.
В новейшем выпуске Spring его конструктор не нужно аннотировать аннотацией @Autowired
.
3. Внедрение конструктора с помощью Lombok
С помощью Lombok
можно создать конструктор либо для всех полей класса (с помощью @AllArgsConstructor
), либо для всех полей конечного
класса (с помощью @RequiredArgsConstructor
). Более того, если вам по-прежнему нужен пустой конструктор, вы можете добавить дополнительную аннотацию @NoArgsConstructor
.
Создадим третий компонент, аналогичный двум предыдущим:
@Component
@RequiredArgsConstructor
public class ThankingService {
private final Translator translator;
public String produce() {
return translator.translate("thank you");
}
}
Приведенная выше аннотация заставит Lombok
сгенерировать для нас конструктор:
@Component
public class ThankingService {
private final Translator translator;
public String thank() {
return translator.translate("thank you");
}
/* Generated by Lombok */
public ThankingService(Translator translator) {
this.translator = translator;
}
}
4. Несколько конструкторов
Конструктор не обязательно должен быть аннотирован, если в компоненте есть только один, и Spring может однозначно выбрать его как правильный для создания экземпляра нового объекта. Как только их становится больше, вам также необходимо аннотировать тот, который будет использоваться контейнером IoC.
Рассмотрим пример ApologizeService :
@Component
@RequiredArgsConstructor
public class ApologizeService {
private final Translator translator;
private final String message;
@Autowired
public ApologizeService(Translator translator) {
this(translator, "sorry");
}
public String produce() {
return translator.translate(message);
}
}
Вышеупомянутый компонент опционально настраивается с полем сообщения
, которое не может измениться после создания компонента (отсюда и отсутствие установщика
). Таким образом, нам потребовалось предоставить два конструктора — один с полной конфигурацией, а другой с неявным значением сообщения
по умолчанию .
Если один из конструкторов не аннотирован с помощью @Autowired
, @Inject
или @Resource
, Spring выдаст ошибку:
Failed to instantiate [...]: No default constructor found;
Если бы мы хотели аннотировать конструктор, сгенерированный Lombok
, нам пришлось бы передать аннотацию с параметром onConstructor
@AllArgsConstructor
:
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApologizeService {
// ...
}
Параметр onConstructor
принимает массив аннотаций (или одну аннотацию, как в этом конкретном примере), которые должны быть помещены в сгенерированный конструктор. Идиома двойного подчеркивания была введена из-за проблем с обратной совместимостью. Согласно документации:
Причина странного синтаксиса заключается в том, чтобы заставить эту функцию работать в компиляторах javac 7; тип
@__
является аннотационной ссылкой на тип аннотации
__
(двойное подчеркивание), который на самом деле не существует; это заставляет javac 7 задерживать прерывание процесса компиляции из-за ошибки, потому что возможно, что процессор аннотаций позже создаст
__
тип.
5. Резюме
В этом руководстве мы показали, что нет необходимости отдавать предпочтение внедрению зависимостей на основе поля, а не внедрению зависимостей на основе конструктора с точки зрения увеличения стандартного кода.
Благодаря Lombok можно автоматизировать генерацию стандартного кода без влияния на производительность среды выполнения, сокращая длинный, непонятный код до использования однострочной аннотации.
Код, использованный во время обучения, доступен на GitHub .