Статьи

Инъекция с CDI (часть II)

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

Полевая инъекция

До сих пор во всех предыдущих постах вы видели аннотацию @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;
}
...
}

Но правило таково: у вас может быть только одна точка инжекции конструктора . Контейнер выполняет инъекцию, а не вы (вы не можете вызвать конструктор в управляемой среде, ну, вы можете, но он не будет работать так, как вы ожидаете). Разрешен только один конструктор bean-компонента, поэтому контейнер может выполнять свои функции, вставляя правильные ссылки. Следующее недействительно  :

@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;
}
...
}

Если у вас есть более одного компонента bean-компонента, это то, что вы получаете (код ошибки и сообщение, конечно, специфичны для 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;
}
...
}

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

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

Вывод

Один вопрос, который вы могли бы задать (и это тот вопрос, который я задал Питу Мьюру ): «когда вы используете поле над конструктором или сеттером?». Там нет реального технического ответа, кроме того, что это ваш личный вкус. В управляемой среде контейнер — это тот, кто выполняет всю инъекцию, ему просто нужны правильные точки впрыска. Тем не менее, с помощью инжектора конструктора или сеттера вы можете добавить некоторую логику, если это необходимо (не с помощью инжекции поля). Но похоже, что добавление сеттера было добавлено для обратной совместимости с существующими компонентами Java .

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

Скачать

Загрузите код , попробуйте и дайте мне обратную связь.

Рекомендации

 

С http://agoncal.wordpress.com/2011/05/03/injection-with-cdi-part-ii/