javax.enterprise.context.RequestScoped 在字段上指定时如何工作?

How does javax.enterprise.context.RequestScoped works when is specified on a field?

在代码中发现如下(实名替换为dummy):

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改成constructor-based会怎么样?在这种情况下,我应该把 @RequestScoped 放在哪里?

@Path("hello")
public class HelloResource {

  private final FirstService service1;
  private final SecondService service2;

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

  ....

}

我相信 @RequestScoped 和其他范围允许在字段(即 @Target({ TYPE, METHOD, FIELD }))上仅用于指定 生产者字段 的范围,即它们只有 @Produces:

才有意义
@Produces
@RequestScoped
Something mySomething;

这里发生了很多很多事情。让我们试着一一解决。

首先,@RequestScoped 是您在制作 的东西上添加的注释。它是一个范围注释,告诉 CDI 正在制作的东西应该存在多长时间。为了保持简单,这可以是 Java class:

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

...或者它可以是生产者方法:

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

(你 可以 把它放在一个字段上,但在这种极其罕见的情况下,你的字段现在充当生产者 本身 。您可以 read about producer fields,但除了在某些 Java EE 场景中,它们几乎总是错误的方法。在您上面列出的情况下,肯定是 采取错误的方法。)

@Inject@RequestScoped 放在任何东西上都没有任何意义。

所以你第一个问题的答案是:没有。

你的第二个问题也可以在通过时被阻止,因为你从来没有在注入场景中使用范围注释(如 @RequestScoped)。您总是在生产场景中使用它们。

换句话说,当你 @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(因为上面没有其他作用域注释)和"probably not"。这几乎肯定不是你想要的。

为了进一步挖掘,您现在必须在档案室 FirstService 中查看您的 META-INF/beans.xml。如果它表明它在它们上面的 bean-discovery-mode is annotated, which is highly likely, then only classes with bean-defining annotations 将被 CDI 发现。因此,由于 FirstService 上没有任何类型的注释,很可能它不会被发现,并且 CDI 会在运行时或启动时的某个时候崩溃,表明没有找到 [=19= 的依赖项].

假设我们将 @ApplicationScoped 放在 FirstService 上。这将使 FirstService 基本上成为一个单例(同样,保持简单)。但是等等,你说,HttpServletRequest 呢?那将在什么范围内?答案是:作为消费者的你不知道,也不关心。 (真正的答案当然是它会反映当前请求,所以很可能在请求范围内。)任何时候你试图访问那个 HttpServletRequest 字段,你最好在一个请求中,或者它会炸毁你的。

或者您可以将 @RequestScoped 放在 FirstService 上,在这种情况下,访问 FirstService 类型字段的任何内容最好在访问时处于活动请求范围内,或者,再次,这一切都会在你身上爆炸。

最后,您是在 JAX-RS 的上下文中执行所有这些操作,它在 CDI 诞生之前就有自己的依赖项注入框架。为了让 JAX-RS 和 CDI 相对更好地协同工作,需要将方形钉子敲入圆孔中。其中一种情况是,严格来说资源 classes 不支持 CDI 样式的构造函数注入,仅支持 JAX-RS 样式的构造函数注入,这是它自己的(已弃用)主题。因此,对于资源 classes,您通常希望保留字段注入。

此外,JAX-RS 应用程序不需要 Servlet 构造。事实上,根据基础架构的特定组合,您 运行、@Inject private HttpServletRequest request 可能也不起作用,您可能必须使用 @Context。 (这是它自己的一套问题和答案。)