- PVSM.RU - https://www.pvsm.ru -

Внедрение зависимостей в CDI. Часть 2

Это второй пост о внедрении зависимостей в CDI (Часть 1 [1]) после нашего разговора о том, как начать работу с CDI [2] в вашем окружении и как интегрировать CDI в существующее Java EE 6 приложение [3]. В этом посте я хочу рассказать о различных точках внедрения в CDI: поле, конструктор и сеттер. Для этого я буду использовать часть предыдущего примера: внедрение POJO генератора ISBN в сервлет.

COFFEE_BEANS

Внедрение через поле

Во всех предыдущих примерах вы видели аннотации @Inject, привязанные к полям (атрибутам) класса.

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    @Inject
    @ThirteenDigits
    private NumberGenerator numberGenerator;

    @Inject
    private ItemEJB itemEJB;
    ...
}

Как вы видите в представленном коде, аннотацией @Inject и спецификатором (здесь @ThirteenDigits) помечен атрибут. Но, как и во многих других фреймворках по внедрению зависимостей, в CDI вы можете проводить внедрение через конструктор или сеттер.

Внедрение через конструктор

Вместо атрибутов вы можете добавить аннотацию @Inject к конструктору. Если же вам необходимо специфированное внедрение, то вы можете пометить спецификатором параметр конструктора:

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    private NumberGenerator numberGenerator;
    private ItemEJB itemEJB;

    @Inject
    public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
        this.numberGenerator = numberGenerator;
        this.itemEJB = itemEJB;
    }
    ...
}

Как вы видите, в данном случае аннотацией @Inject помечен не атрибут класса, а конструктор. С другой стороны, @ThirteenDigits помечает не конструктор, а его параметр numberGenerator (что логично). Если захотите, вы можете смешивать внедрение через поле и через конструктор (ниже я использую внедрение через конструктор и атрибут для EJB):

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    private NumberGenerator numberGenerator;

    @Inject
    private ItemEJB itemEJB;

    @Inject
    public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator) {
        this.numberGenerator = numberGenerator;
    }
    ...
}

Но есть правило: у вас может быть только один конструктор с внедрением. Контейнер выполняет внедрение, не вы (вы, конечно, можете вызывать конструктор в управляемой среде, но он не будет работать так, как вы ожидаете). И есть только один конструктор бина, позволяющий контейнеру выполнить корректное внедрение всех зависимостей. Следующий код некорректный:

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    private NumberGenerator numberGenerator;
    private ItemEJB itemEJB;

    @Inject
    public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
        this.numberGenerator = numberGenerator;
        this.itemEJB = itemEJB;
    }

    @Inject
    public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator) {
        this.numberGenerator = numberGenerator;
    }
    ...
}

Если у вас более одного конструктора в бине, вот что вы получите (код и сообщение об ошибке, конечно, специфичны для Weld)

WELD-000812 Cannot determine constructor to use for public@WebServlet class ItemServlet. Possible constructors [[constructor] @Inject public ItemServlet(NumberGenerator, ItemEJB), [constructor] @Inject public ItemServlet(NumberGenerator)]

Да, синтаксически допустимо проводить внедрение через поле и конструктор одновременно, но смысла в этом нет:

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    @Inject @ThirteenDigits
    private NumberGenerator numberGenerator;
    @Inject
    private ItemEJB itemEJB;

    @Inject
    public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
        this.numberGenerator = numberGenerator;
        this.itemEJB = itemEJB;
    }
    ...
}

Внедрение через сеттер

Есть другой способ — это использовать внедрение через сеттер, которое выглядит как внедрение через конструктор. Аннотацией @Inject вы помечаете сам сеттер, а спецификаторами — его аргументы:

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

    private NumberGenerator numberGenerator;
    private ItemEJB itemEJB;

    @Inject
    public void setNumberGenerator(@ThirteenDigits NumberGenerator numberGenerator) {
        this.numberGenerator = numberGenerator;
    }

    @Inject
    public void setItemEJB(ItemEJB itemEJB) {
        this.itemEJB = itemEJB;
    }
    ...
}

Когда вы используете внедрение через конструктор или сеттер, вам необходимо специфицировать аргументы. Поэтому убедитесь, что у вас правильно объявлен @Target(java.lang.annotation.ElementType.PARAMETER):

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface ThirteenDigits {
}

Заключение

У вас может возникнуть один вопрос (и я его [4] тоже задал Pete Muir [5]): когда нужно использовать ту или иную точку внедрения? Но на этот вопрос нет технического ответа, это дело личного вкуса. В управляемой среде только контейнер выполняет внедрение, и всё, что ему нужно — это корректные точки внедрения. Однако в случае внедрения через конструктор или сеттер, при необходимости вы можете добавить какую-то логику (что невозможно при внедрении через атрибуты). Но, похоже, что внедрение через сеттеры было добавлено, скорее, для обратной совместимости с уже созданными Java Beans [6].

В следующей статье я расскажу о продюсерах [7].

Исходный код

Скачайте код [8] и расскажите, что вы о нем думаете.

Автор: AT Consulting

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/129051

Ссылки в тексте:

[1] Часть 1: https://habrahabr.ru/company/at_consulting/blog/301636/

[2] начать работу с CDI: https://antoniogoncalves.org/2011/01/12/bootstrapping-cdi-in-several-environments/

[3] интегрировать CDI в существующее Java EE 6 приложение: https://antoniogoncalves.org/2011/02/07/adding-cdi-to-an-existing-java-ee-6-application/

[4] его: http://twitter.com/#!/agoncal/status/65166255216279553

[5] Pete Muir: http://twitter.com/#!/plmuir/status/65196933928595456

[6] для обратной совместимости с уже созданными Java Beans: http://twitter.com/#!/plmuir/statuses/65497453637550080

[7] продюсерах: https://habrahabr.ru/company/at_consulting/blog/302010/

[8] Скачайте код: https://github.com/agoncal/agoncal-sample-cdi

[9] Источник: https://habrahabr.ru/post/301768/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best