Как работает javax.enterprise.context.RequestScoped, если он указан в поле? - PullRequest
0 голосов
/ 19 марта 2020

Найдено следующее в коде (реальные имена заменены на фиктивные):

Ресурс JAX-RS

@Path("hello")
public class HelloResource {

  @Inject
  @RequestScoped
  FirstService service1;

  @Inject
  SecondService service2;

  ....

}

Зависимости

// first
public class FirstService {

  private static final Logger LOGGER = ...

  @Inject
  HttpServletRequest request;

  ....
}

// second
@ApplicationScoped
public class SecondService { .... } 

Разрешено объявить @RequestScoped на поле. Но нигде не смог найти, как это работает.

Вопрос 1: Если я укажу @RequestScoped в поле, которое будет введено контейнером, получу ли я реально-внедренный экземпляр область запроса там?

Вопрос 2: Что если я поменяю DI на конструктор? Куда я положу @RequestScoped в этом случае?

@Path("hello")
public class HelloResource {

  private final FirstService service1;
  private final SecondService service2;

  @Inject
  public HelloResource(FirstService service1, SecondService service2) {
    // set values here
  }

  ....

}

Ответы [ 2 ]

3 голосов
/ 20 марта 2020

Здесь происходит много-много-много вещей. Давайте попробуем разобраться с ними один за другим.

Во-первых, @RequestScoped - это аннотация, которую вы надеваете на то, что сделано . Это аннотация объема, которая сообщает CDI, как долго должна существовать созданная вещь. Пытаясь сохранить простоту, это может быть Java класс:

@RequestScoped
public class Frobnicator { /* ... */ }

… или это может быть метод производителя:

@Produces
@RequestScoped
Frobnicator makeRequestScopedFrobnicator() { /* ... */ }

(Вы можете поместите его в поле, но в этом исключительно редком случае ваше поле теперь действует как сам производитель . Вы можете прочитать о полях производителя , но за исключением некоторых Java EE Scenar ios они почти всегда неверный подход. В вашем случае, как указано выше, это безусловно неправильный подход.)

Положить @Inject и @RequestScoped ни в чем не имеет никакого смысла.

Так что ответ на ваш первый вопрос: нет.

Ваш второй вопрос также может быть отклонен на перевале, потому что вы никогда не используйте аннотации области видимости (например, @RequestScoped) в сценарии внедрения ios. Вы всегда используете их в производственном сценарии ios.

Другими словами, когда вы @Inject что-то, по определению вы в основном не знаете, в каком объеме находится вещь, которую вы только что ввели; вы просто используете его как обычный POJO, а CDI позаботится о том, чтобы дать вам правильную вещь.

Итак, в вашем случае это выглядит так, как будто вы хотите это:

@Path("hello")
public class HelloResource {

  @Inject
  FirstService service1;

  @Inject
  SecondService service2;

  /* etc. */
}

… и:

// We'll talk about the lack of annotations here in a moment
public class FirstService {

  private static final Logger LOGGER = ...

  @Inject
  HttpServletRequest request;

  /* etc. */
}

Это хорошо, насколько это возможно, но какой объем имеет FirstService? И знает ли CDI об этом вообще?

Быстрые ответы на на этот вопрос , соответственно: @Dependent (поскольку на нем нет других аннотаций области видимости) и "вероятно, нет". Это почти наверняка не то, что вы хотите.

Чтобы копнуть немного дальше, теперь вам нужно взглянуть на ваш META-INF/beans.xml в архивном корпусе FirstService. Если он указывает, что его bean-discovery-mode равен annotated, что весьма вероятно, то только классы с аннотациями, определяющими компонент на них, будут обнаружены КДИ. Так как FirstService не имеет каких-либо аннотаций, скорее всего, он не будет обнаружен, и CDI взорвется когда-нибудь во время выполнения или запуска, указывая, что не было найдено никакой зависимости для FirstService.

Допустим, мы поставили @ApplicationScoped на FirstService. Это сделает FirstService в основном синглтоном (опять же, сохраняя его простым). Но подождите, вы говорите, что насчет HttpServletRequest? В какой сфере это будет? Ответ: вы, как потребитель, не знаете, и вам все равно. (Реальным ответом будет, конечно, то, что он будет отражать текущий запрос, поэтому, по всей вероятности, находится в области действия запроса.) Каждый раз, когда вы пытаетесь получить доступ к этому полю HttpServletRequest, вам лучше быть в запросе, или он взорвется вверх на вас.

Или вы можете поставить @RequestScoped на FirstService, и в этом случае все, что обращается к полю с типом FirstService, должно быть лучше в активной области запроса во время доступа, или, опять же, все это взорвется от вас.

Наконец, вы делаете все это в контексте JAX-RS, у которого была собственная структура внедрения зависимостей до появления CDI. Для того, чтобы JAX-RS и CDI играли вместе достаточно красиво, требовалось много колотых квадратных колышков в круглых отверстиях. Одним из таких случаев является то, что, строго говоря, классы ресурсов не поддерживают инъекцию конструктора в стиле CDI, только инъекцию конструктора в стиле JAX-RS, которая является собственной (устаревшей) темой. Таким образом, с классами ресурсов вы, как правило, хотите остаться с внедрением поля.

Кроме того, приложения JAX-RS не требуют конструкций Servlet. Фактически, в зависимости от конкретной комбинации инфраструктуры, с которой вы работаете, @Inject private HttpServletRequest request также может не работать, и вам, возможно, придется использовать @Context. (Это его собственный набор вопросов и ответов.)

0 голосов
/ 19 марта 2020

Полагаю, @RequestScoped и другие области разрешены для полей (т. Е. @Target({ TYPE, METHOD, FIELD })) только для указания области действия поля производителя , т.е. они имеют смысл только с @Produces:

@Produces
@RequestScoped
Something mySomething;
...