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

joh*_*ohn 4 java scope cdi java-ee-8

在代码中发现以下内容(真实姓名替换为虚拟姓名):

JAX-RS 资源

@Path("hello")
public class HelloResource {

  @Inject
  @RequestScoped
  FirstService service1;

  @Inject
  SecondService service2;

  ....

}
Run Code Online (Sandbox Code Playgroud)

依赖关系

// first
public class FirstService {

  private static final Logger LOGGER = ...

  @Inject
  HttpServletRequest request;

  ....
}

// second
@ApplicationScoped
public class SecondService { .... } 
Run Code Online (Sandbox Code Playgroud)

允许@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
  }

  ....

}
Run Code Online (Sandbox Code Playgroud)

Lai*_*son 8

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

\n\n

首先,是您在正在制作的@RequestScoped内容上添加的注释。它是一个范围注释,告诉 CDI 所制作的东西应该存在多久。为了简单起见,这可以是一个 Java 类:

\n\n
@RequestScoped\npublic class Frobnicator { /* ... */ }\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\xa6 或者它可以是一个生产者方法:

\n\n
@Produces\n@RequestScoped\nFrobnicator makeRequestScopedFrobnicator() { /* ... */ }\n
Run Code Online (Sandbox Code Playgroud)\n\n

(您可以将它放在一个字段上,但在这种极其罕见的情况下,您的字段现在充当生产者本身。您可以阅读有关生产者字段的内容,但除了在某些 Java EE 场景中之外,它们几乎总是错误的方法。你的情况如上面所列,这肯定是错误的做法。)

\n\n

穿上任何东西@Inject@RequestScoped没有任何意义。

\n\n

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

\n\n

您的第二个问题也可以在传递时结束,因为您在注入场景中从未使用范围注释(如@RequestScoped)。您总是在生产场景中使用它们。

\n\n

换句话说,当你@Inject做某事时,根据定义,你基本上不知道你刚刚注入的东西在什么范围内;您只需将其用作普通 POJO,CDI 就会为您提供正确的信息。

\n\n

因此,就您而言,您似乎想要这样:

\n\n
@Path("hello")\npublic class HelloResource {\n\n  @Inject\n  FirstService service1;\n\n  @Inject\n  SecondService service2;\n\n  /* etc. */\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\xa6 和:

\n\n
// We\'ll talk about the lack of annotations here in a moment\npublic class FirstService {\n\n  private static final Logger LOGGER = ...\n\n  @Inject\n  HttpServletRequest request;\n\n  /* etc. */\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

就目前而言这很好,但是范围有多大呢FirstService?CDI 真的知道这件事吗?

\n\n

问题的快速答案分别是:(@Dependent因为没有其他范围注释)和“可能不是”。这几乎肯定不是您想要的。

\n\n

为了进一步挖掘,你现在必须查看你的META-INF/beans.xml档案室FirstService。如果它表明它bean-discovery-modeannotated,这很有可能,那么只有具有bean 定义注释的类才会被 CDI 发现。因此,由于FirstService它上面没有任何类型的注释,很可能不会被发现,并且 CDI 会在运行时或启动时爆炸,表明没有找到FirstService.

\n\n

假设我们穿上@ApplicationScopedFirstService. 这基本上将成为FirstService一个单例(再次,保持简单)。但是等等,你说,那呢HttpServletRequest?那会在什么范围内?答案是:作为消费者的你不知道,也不在乎。(真正的答案当然是它将反映当前的请求,因此很可能在请求范围内。)任何时候你尝试访问该HttpServletRequest字段,你最好在请求中,否则它会崩溃你。

\n\n

或者你可以加上@RequestScopedFirstService在这种情况下,任何访问FirstService-typed 字段的内容最好在访问时处于活动请求范围内,否则,它都会在你身上爆炸。

\n\n

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

\n\n

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

\n