«1. Введение
Lombok — чрезвычайно полезная библиотека для преодоления шаблонного кода. Если вы еще не знакомы с ним, я настоятельно рекомендую взглянуть на предыдущий урок — Introduction to Project Lombok.
В этой статье мы продемонстрируем его удобство использования в сочетании с инъекцией зависимостей на основе конструктора Spring.
2. Внедрение зависимостей на основе конструктора
Хороший способ связывания зависимостей в Spring с помощью внедрения зависимостей на основе конструктора. Этот подход заставляет нас явно передавать зависимости компонента конструктору.
В отличие от внедрения зависимостей на основе поля, он также предоставляет ряд преимуществ:
-
нет необходимости создавать компонент конфигурации для конкретного теста — зависимости вводятся явно в согласованном с конструктором дизайне — все необходимое зависимости подчеркнуты и отслеживаются с помощью определения конструктора простые модульные тесты — уменьшенные накладные расходы 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");
}
}
Давайте создадим третий компонент, аналогичный двум предыдущим:
@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;
}
}
Приведенная выше аннотация заставит Lombok сгенерировать для нас конструктор:
4. Несколько конструкторов
Конструктор не не нужно аннотировать, если в компоненте есть только один, и Spring может однозначно выбрать его как правильный для создания экземпляра нового объекта. Как только их становится больше, вам также необходимо аннотировать тот, который будет использоваться контейнером IoC.
@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);
}
}
Рассмотрим пример ApologizeService:
Вышеупомянутый компонент опционально настраивается с полем сообщения, которое не может измениться после создания компонента (отсюда отсутствие установщика). Таким образом, нам потребовалось предоставить два конструктора — один с полной конфигурацией, а другой с неявным значением сообщения по умолчанию.
Failed to instantiate [...]: No default constructor found;
Если один из конструкторов не аннотирован с помощью @Autowired, @Inject или @Resource, Spring выдаст ошибку:
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApologizeService {
// ...
}
Если бы мы хотели аннотировать конструктор, сгенерированный Lombok, нам пришлось бы передать аннотация с параметром onConstructor @AllArgsConstructor:
The reason of the weird syntax is to make this feature work in javac 7 compilers; the
@__
type is an annotation reference to the annotation type__
(double underscore) which doesn’t actually exist; this makes javac 7 delay aborting the compilation process due to an error because it is possible an annotation processor will later create the__
type.
Параметр onConstructor принимает массив аннотаций (или одну аннотацию, как в этом конкретном примере), которые должны быть помещены в сгенерированный конструктор. Идиома двойного подчеркивания была введена из-за проблем с обратной совместимостью. Согласно документации:
5. Резюме
В этом руководстве мы показали, что нет необходимости отдавать предпочтение DI на основе поля перед DI на основе конструктора с точки зрения увеличения стандартного кода.
Благодаря Lombok можно автоматизировать генерацию стандартного кода без влияния на производительность среды выполнения, сокращая длинный, непонятный код до использования однострочной аннотации.