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)
这里发生了很多很多很多事情。让我们尝试一一解决它们。
\n\n首先,是您在正在制作的@RequestScoped内容上添加的注释。它是一个范围注释,告诉 CDI 所制作的东西应该存在多久。为了简单起见,这可以是一个 Java 类:
@RequestScoped\npublic class Frobnicator { /* ... */ }\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 或者它可以是一个生产者方法:
\n\n@Produces\n@RequestScoped\nFrobnicator makeRequestScopedFrobnicator() { /* ... */ }\nRun Code Online (Sandbox Code Playgroud)\n\n(您可以将它放在一个字段上,但在这种极其罕见的情况下,您的字段现在充当生产者本身。您可以阅读有关生产者字段的内容,但除了在某些 Java EE 场景中之外,它们几乎总是错误的方法。你的情况如上面所列,这肯定是错误的做法。)
\n\n穿上任何东西@Inject都@RequestScoped没有任何意义。
所以你的第一个问题的答案是:不。
\n\n您的第二个问题也可以在传递时结束,因为您在注入场景中从未使用范围注释(如@RequestScoped)。您总是在生产场景中使用它们。
换句话说,当你@Inject做某事时,根据定义,你基本上不知道你刚刚注入的东西在什么范围内;您只需将其用作普通 POJO,CDI 就会为您提供正确的信息。
因此,就您而言,您似乎想要这样:
\n\n@Path("hello")\npublic class HelloResource {\n\n @Inject\n FirstService service1;\n\n @Inject\n SecondService service2;\n\n /* etc. */\n}\nRun 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}\nRun Code Online (Sandbox Code Playgroud)\n\n就目前而言这很好,但是范围有多大呢FirstService?CDI 真的知道这件事吗?
该问题的快速答案分别是:(@Dependent因为没有其他范围注释)和“可能不是”。这几乎肯定不是您想要的。
为了进一步挖掘,你现在必须查看你的META-INF/beans.xml档案室FirstService。如果它表明它bean-discovery-mode是annotated,这很有可能,那么只有具有bean 定义注释的类才会被 CDI 发现。因此,由于FirstService它上面没有任何类型的注释,很可能不会被发现,并且 CDI 会在运行时或启动时爆炸,表明没有找到FirstService.
假设我们穿上@ApplicationScoped了FirstService. 这基本上将成为FirstService一个单例(再次,保持简单)。但是等等,你说,那呢HttpServletRequest?那会在什么范围内?答案是:作为消费者的你不知道,也不在乎。(真正的答案当然是它将反映当前的请求,因此很可能在请求范围内。)任何时候你尝试访问该HttpServletRequest字段,你最好在请求中,否则它会崩溃你。
或者你可以加上@RequestScoped,FirstService在这种情况下,任何访问FirstService-typed 字段的内容最好在访问时处于活动请求范围内,否则,它都会在你身上爆炸。
最后,您将在 JAX-RS 的上下文中完成所有这些工作,JAX-RS 在 CDI 诞生之前就有自己的依赖项注入框架。为了使 JAX-RS 和 CDI 能够相对良好地协同工作,需要将大量的方钉敲入圆孔中。其中一种情况是,严格来说,资源类不支持 CDI 样式的构造函数注入,仅支持 JAX-RS 样式的构造函数注入,这是它自己的(已弃用的)主题。因此,对于资源类,您通常希望保留字段注入。
\n\n此外,JAX-RS 应用程序不需要 Servlet 构造。事实上,根据您运行的基础设施的特定组合,@Inject private HttpServletRequest request可能也不起作用,您可能必须使用@Context. (这是它自己的一组问题和答案。)