Внедрение в конструктор весной с помощью Lombok

Инъектор конструктора весной с Ломбоком

1. Вступление

Lombok - чрезвычайно полезная библиотека для преодоления стандартного кода. Если вы еще не знакомы с ним, я настоятельно рекомендую взглянуть на предыдущий урок -Introduction to Project Lombok.

В этой статье мы продемонстрируем удобство использования в сочетании сConstructor-Based Dependency Injection Spring.

2. Внедрение зависимостей на основе конструктора

Хороший способ связать зависимости в Spring с помощью constructor-based Dependency Injection. Этот подход заставляет нас явно передавать зависимости компонентов в конструктор.

В отличие отField-Based Dependency Injection, он также дает ряд преимуществ:

  • нет необходимости создавать специфичный для теста компонент конфигурации - зависимости явно вводятся в конструктор

  • согласованный дизайн - все необходимые зависимости подчеркнуты и поддерживаются определением конструктора

  • простые модульные тесты - снижение накладных расходов Spring Framework

  • восстановлена ​​свобода использования ключевых словfinal

Однако из-за необходимости написания конструктора он приводит к значительно большей базе кода. Рассмотрим два примера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");
    }
}

По сути, оба компонента делают одно и то же - они вызывают настраиваемыйTranslator со словом, специфичным для задачи.

Второй вариант, однако, гораздо более запутан из-за шаблона конструктора, который на самом деле не приносит никакой ценности коду.

В новейшем выпуске Spring конструктор не нуждается в аннотации@Autowired.

3. Внедрение конструктора с помощью Lombok

С помощьюLombok можно сгенерировать конструктор либо для всех полей класса (с@AllArgsConstructor), либо для всех полей классаfinal@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);
    }
}

Вышеупомянутый компонент необязательно настраивается с помощью поляmessage, которое не может измениться после создания компонента (отсюда и отсутствиеsetter). Таким образом, нам потребовалось предоставить два конструктора - один с полной конфигурацией, а другой с неявным значением по умолчанию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. Резюме

В этом учебном пособии мы показали, что нет необходимости отдавать предпочтение DI на основе полей по сравнению с DI на основе конструктора с точки зрения увеличения стандартного кода.

Благодаря Lombok можно автоматизировать генерацию общего кода без снижения производительности во время выполнения, сократив длинный, неясный код до использования однострочной аннотации.

Код, используемый во время обучения, доступенover on GitHub.