«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 можно автоматизировать генерацию стандартного кода без влияния на производительность среды выполнения, сокращая длинный, непонятный код до использования однострочной аннотации.