在 Java EE CDI 中,如何始终知道 CDI bean 应声明为什么范围?
In Java EE CDI how is it possible to always know what scope a CDI bean should be declared as?
我正在学习 Java EE 8 教程,新手对作用域有些困惑。我对此很陌生,所以如果这是一个愚蠢的问题,请多多包涵。
我的理解是,CDI 允许任何 class 被注解为一个范围,例如 @RequestScoped
被注入到一个 Servlet 中。
https://javaee.github.io/tutorial/cdi-basic001.html 教程中给出的例子是
@RequestScoped
public class MessageB implements Message { ... }
允许这样做:
@WebServlet("/cdiservlet")
public class NewServlet extends HttpServlet {
@Inject private Message message;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().write(message.get());
}
}
这一切都很好,也很容易理解,但是......
我的问题是,这不是限制 and/or 在 servlet 中使用 MessageB class 吗?如果要用作 CDI bean 的 class 需要在一个用例、servlet 或应用程序的请求范围内使用,而在另一个用例的会话范围内使用,并且可能在另一个用例的应用程序范围内使用怎么办?这应该如何工作? class 开发者作者是否应该设想尽可能广泛的范围(尤其是在编写将被其他开发者使用的 class 时)?如果是这样,如果使用 class 的客户端想要在请求范围内使用它怎么办?
看起来我们应该有松散耦合的代码,但是将 class 绑定到范围限制了 class 可以以可能无用或不合逻辑的方式使用的方式。
还是总是假定像这样使用的 bean 是由应用程序开发人员编写的 him/herself?
提前感谢您的任何见解。
编辑: 更进一步看来 @Qualifier
是这些问题的解决方案。开发人员需要提供不同的子类型(例如,通过实现接口或扩展 class),然后对每个不同的类型使用 @Qualifier
属性。
是的,您可以使用不同的 CDI 作用域 Message
接口的不同 CDI Bean。这种方法的最佳实践是定义多个注释,例如
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface SessionMessage {}
然后您可以添加 @SessionMessage
例如在您的 @SessionScope
bean 上方并使用 @Inject @SessionMessage private Message message
.
注入此消息 bean
另一种方法可能是 CDI 的默认 @Dependet
范围,如果您没有在 CDI bean 上定义任何范围,则使用它。使用此范围,您可以实现以下目标:
@Dependent: The default scope if none is specified; it means that an object exists to serve exactly one client (bean) and has the same lifecycle as that client (bean). (https://docs.oracle.com/javaee/6/tutorial/doc/gjbbk.html)
使用此注释,您可以继承客户端 bean 的范围。因此,如果您将 Message
注入 @RequestScoped
bean,您的 Message
将具有相同的生命周期。
我正在学习 Java EE 8 教程,新手对作用域有些困惑。我对此很陌生,所以如果这是一个愚蠢的问题,请多多包涵。
我的理解是,CDI 允许任何 class 被注解为一个范围,例如 @RequestScoped
被注入到一个 Servlet 中。
https://javaee.github.io/tutorial/cdi-basic001.html 教程中给出的例子是
@RequestScoped
public class MessageB implements Message { ... }
允许这样做:
@WebServlet("/cdiservlet")
public class NewServlet extends HttpServlet {
@Inject private Message message;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().write(message.get());
}
}
这一切都很好,也很容易理解,但是......
我的问题是,这不是限制 and/or 在 servlet 中使用 MessageB class 吗?如果要用作 CDI bean 的 class 需要在一个用例、servlet 或应用程序的请求范围内使用,而在另一个用例的会话范围内使用,并且可能在另一个用例的应用程序范围内使用怎么办?这应该如何工作? class 开发者作者是否应该设想尽可能广泛的范围(尤其是在编写将被其他开发者使用的 class 时)?如果是这样,如果使用 class 的客户端想要在请求范围内使用它怎么办?
看起来我们应该有松散耦合的代码,但是将 class 绑定到范围限制了 class 可以以可能无用或不合逻辑的方式使用的方式。
还是总是假定像这样使用的 bean 是由应用程序开发人员编写的 him/herself?
提前感谢您的任何见解。
编辑: 更进一步看来 @Qualifier
是这些问题的解决方案。开发人员需要提供不同的子类型(例如,通过实现接口或扩展 class),然后对每个不同的类型使用 @Qualifier
属性。
是的,您可以使用不同的 CDI 作用域 Message
接口的不同 CDI Bean。这种方法的最佳实践是定义多个注释,例如
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface SessionMessage {}
然后您可以添加 @SessionMessage
例如在您的 @SessionScope
bean 上方并使用 @Inject @SessionMessage private Message message
.
另一种方法可能是 CDI 的默认 @Dependet
范围,如果您没有在 CDI bean 上定义任何范围,则使用它。使用此范围,您可以实现以下目标:
@Dependent: The default scope if none is specified; it means that an object exists to serve exactly one client (bean) and has the same lifecycle as that client (bean). (https://docs.oracle.com/javaee/6/tutorial/doc/gjbbk.html)
使用此注释,您可以继承客户端 bean 的范围。因此,如果您将 Message
注入 @RequestScoped
bean,您的 Message
将具有相同的生命周期。