CDI 中的@ApplicationScoped 和@Singleton 作用域有什么区别?

在 CDI 中有 @ApplicationScoped和(javax.inject) @Singleton伪作用域。他们之间有什么区别?除此之外,@ApplicationScoped是代理的,而 @Singleton不是。

我可以把我的 @Singleton bean 改成 @ApplicationScoped吗? @ApplicationScoped bean 可以有两个(或更多)实例吗?

93480 次浏览

@Singleton不是 CDI 规范的一部分。它是 EJB 和 javax.inject(JSR-330)的一部分。规范中没有提到它的行为,因此您只能依赖于 Weld 文档中所写的内容。

JSR-299中的 @Singleton指的是 Singleton 会话 bean (javax.ejb.Singleton,而不是 javax.inject.Singleton) ,而不是内置作用域 Singleton 中的 JSR-299管理 bean。

您可能会在服务器中发现,@ApplicationScoped是每个 EAR 一个或每个 WAR/EJB-JAR 一个,因为在规范中并不清楚,但是您绝对不应该期望它是每个 JVM 一个。

使用缺省构造函数编写类的一个主要区别是在使用 javax.inject.Singleton时使用了私有访问修饰符,但是在使用 javax.enterprise.context.ApplicationScoped时,你的类应该至少使用默认访问修饰缺省构造函数,这就是 JBOSS 6.1 GA Final的实现

通常,当您只想拥有某个对象的一个实例时,您可能应该使用 @ApplicationScoped注释-这样的对象是代理的,因此甚至可以正确地开箱即用地序列化。

另一方面,在许多情况下,您只想要类的一个实例,但是这样的类不能被代理(例如,因为是 final)-那么 @Singleton就是一个拯救。因为 Singleton是一个伪范围,不像任何“普通”范围那样被代理。

简而言之: 你甚至可以混合它(@Singleton@ApplicationScoped) ,在某些情况下它是有意义的。 (和我的一样有效!)

除了目前为止的其他答案之外,我还想在现实世界的场景中添加一些澄清的要点。

For me this question developed out of How do I force an application-scoped bean to instantiate at application startup? 在一些讨论中,我提到了这一点,但到目前为止还没有找到一个有效的反对理由:

In a lot of real-life scenarios/setups I would say it's hard to 从抽象/建模的角度来看,明确地说 某些东西是(或将成为/被视为) EJB 或应用程序范围的托管 bean

(有争议但不是决定性的)(从我的观点来看)迄今为止反对它的论据: (@BalusC 和其他所有人: 我希望看到它们是决定性的,但如果不是,上面的观点可能是正确的,尽管如此,这些论点仍然可以帮助读者获得差异/优点/缺点/不好/好的做法)

EJB 与托管 Bean

BalusC : 这是一个 EJB,而不是一个托管 bean,这是完全不同的。EJB 在后端运行,而托管 bean 在前端。EJB 也在事务上下文中运行。 你刚刚把企业 bean 和管理 bean 搞混了我刚刚指出。

但是:

我认为你不完全正确,夸大了意思/用法,在我看来这是有争议的。返回文章页面企业 JavaBeans 的 http://en.wikipedia.org/wiki/enterprise_javabeans 译者:

EJB (EJB)是一种用于企业软件模块化构建的受管理服务器软件,也是多种 Java API 之一。EJB 是封装应用程序业务逻辑的服务器端基于组件的软件工程。

企业豆的种类

会话 Bean [3]可以是“有状态”、“无状态”或“单例”[ ... ]

消息驱动 Bean [ ... ]

... which still holds true in my case.

单例 EJB 与应用程序作用域 Bean

锁定

BalusC : 单例 EJB 与应用程序范围内的 bean 不同。单例 EJB 是读/写锁定的,因此对于您想到的任务可能效率低下/过于复杂。长话短说: 抓住一本好的 JavaEE 书籍,并学会使用正确的工具来完成工作。一种方式肯定不是另一种方式。它能工作并不意味着它就是正确的工具。大锤能够固定螺丝,但它不一定是固定螺丝的正确工具:)

但是:

(我在这里看不到大锤-对不起...) 知道锁的默认值是好的(我没有意识到这一点) ,但是这似乎又是不正确的: < a href = “ https://docs.Oracle.com/cd/E19798-01/821-1841/gipsz/index.html”rel = “ noReferrer”> Oracle JavaEE6教程 on < em > 在单一会话 Bean 中管理并发访问

在创建单例会话 bean 时,可以通过两种方式控制对单例业务方法的并发访问: 容器管理的并发和 bean 管理的并发。 [ ... ]

尽管默认情况下,单例使用容器管理的并发,但是可以在单例的类级别添加@ConcurrencyManagement (CONTAINER)注释来显式设置并发管理类型

还有一点不同: @Singleton不是 bean 定义注释,因为 Singleton作用域不是正常的作用域。 那么 @ApplicationScoped就是 bean 定义注释。

使用 CDI 1.1规范: 当应用程序处于 Discovery-mode = 注释状态时,Weld 不使用 @Singleton标识 bean,也不加载这个命令