Впровадження залежностей в CDI. Частина 2

Це другий пост про впровадження залежностей в CDI (Частина 1) після нашої розмови про те, як почати роботу з CDI у вашому оточенні і як інтегрувати CDI в існуюче Java EE 6 додаток. У цьому пості я хочу розповісти про різних точках впровадження в 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 {
}

Висновок
У вас може виникнути питання (і я теж поставив Pete Muir): коли потрібно використовувати ту чи іншу точку впровадження? Але на це питання немає технічної відповіді, це справа особистого смаку. У керованому середовищі контейнер виконує впровадження, і все, що йому потрібно — це коректні точки впровадження. Однак у випадку впровадження через конструктор або сетер, при необхідності ви можете додати якусь логіку (що неможливо при впровадженні через атрибути). Але, схоже, що впровадження через сетери було додано, швидше, для зворотної сумісності з вже створеними Java Beans.
У наступній статті я розповім про продюсерів.
Вихідний код
Скачайте код і розкажіть, що ви про нього думаєте.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.