Java requestscope与bean重用

Java requestscope与bean重用,java,cdi,quarkus,Java,Cdi,Quarkus,我有一个管理对象的类/bean(在本例中EngineManager包含一个Engine对象)。引擎对象不能同时使用,其初始化有点耗时。但是,可以创建EngineManager的多个实例,从而创建多个引擎实例 public class EngineManager { private Engine engine; @PostConstruct public void init() { this.engine = // ... perform cost

我有一个管理对象的类/bean(在本例中EngineManager包含一个Engine对象)。引擎对象不能同时使用,其初始化有点耗时。但是,可以创建EngineManager的多个实例,从而创建多个引擎实例

public class EngineManager
{
    private Engine engine;

    @PostConstruct
    public void init()
    {
        this.engine = // ... perform costly initialization
    }

    public void doSomethingWithEngine()
    {
        // ...
    }
}
我试图找出管理这个对象的类要使用哪个CDI作用域

  • 我不想让这个类成为一个单例类,因为我可以创建它的多个实例,而单例类将是一个瓶颈
  • 由于并发问题,我无法使用@ApplicationScoped
  • 我不想使用RequestScoped,因为据我所知,这会为每个请求创建一个新实例,并且引擎对象的昂贵初始化将带来大量开销
所以我的问题是:有没有一种(CDI)方法

  • 使对EngineManager类的访问线程安全,并且
  • 是否有EngineManager类的多个可重用实例

  • 简而言之:据我所知,如果不付出额外的努力,就无法严格在CDI中解决这个问题。以下是一些一般的想法:

    此问题类似于DB连接池的问题。解决此问题的一种方法是使用一个
    引擎
    实例池,
    引擎管理员从中选择

    如果您使用引擎池,只要池保证每个线程获得不同的
    引擎,就可以对
    @ApplicationScoped

    其中一个有趣的方面是如何处理
    引擎
    实例的不可用性。抛出异常是最简单的答案,但可能不适合您的用例。在
    引擎可用之前阻塞当前线程(可能有一个超时)是另一个次优解决方案,因为它在流量下无法很好地扩展

    如果您的环境允许,您可能需要考虑与池相结合的异步解决方案。提交任务的

    ExecutorService
    (请参阅JEE环境中的
    ManagedExecutorService
    );JMS或其他排队机制的设置可能更复杂(同样取决于您的环境),但可以以消息持久性的形式提供可靠性(如果服务器在您提交工作后但在检索结果之前崩溃,则它可以在重新联机时恢复并完成工作)。完全异步需要更多的努力,但如果您的特定用例证明了这一点,则可能更合适

    对评论的反应:

    • EJB是传统JEE应用程序中此类用例的自然方式。您将使用应用服务器提供的设施。(我的本能是在今天远离EJB…只是说)
    • 你在Quarkus上(我很好)。如果你要排队,你必须设置一个不同的系统——你可以判断它是否值得。Quarkus以多种方式支持异步执行(您甚至可能想要尝试反应流解决方案)
    • 我不知道提到的omniservices图书馆。它可能适合您的需要,但需要转换为Quarkus扩展,不幸的是,此时的Quarkus
    ,所以这个只是对它进行一点扩展。 对于这个问题,确实没有现成的解决方案。据我所知,这里的主要问题是
    引擎
    对象及其共享。您希望能够保存
    n
    实例,并在
    m
    EngineManager
    实例之间分发它们

    public class EngineManager
    {
        private Engine engine;
    
        @PostConstruct
        public void init()
        {
            this.engine = // ... perform costly initialization
        }
    
        public void doSomethingWithEngine()
        {
            // ...
        }
    }
    
    请注意,如果使用
    @Inject
    引擎
    转到
    引擎管理器
    ,则引擎将在管理器的生命周期中绑定到管理器。因此,如果您想动态地交换它(例如,一个管理器使用不同的引擎进行不同的调用),那么您还必须使用动态解析(
    实例
    )。基于此,
    EngineManager
    可以是从属的,也可以是应用程序范围的

    我可以想出两种方法来实现
    引擎
    实例共享和拥有多个实例

    public class EngineManager
    {
        private Engine engine;
    
        @PostConstruct
        public void init()
        {
            this.engine = // ... perform costly initialization
        }
    
        public void doSomethingWithEngine()
        {
            // ...
        }
    }
    
  • 引擎
    创建一个包含
    @相关
    作用域生成器的bean。现在,每次注入
    引擎
    都会调用该生产者,您可以控制它返回的内容。bean可以保存一组引擎,有时它会提供一个现有的引擎(如果它们是免费的),有时它会创建一个新的引擎线程安全取决于您

  • 定义您自己的自定义范围,以满足您的需要。这需要一些专业知识和使用CDI,因为在CDI中,您通常会使用扩展,但在Quarkus中您不能这样做。例如,在其中,您可以在Quarkus中重新实现为自定义作用域,并在希望
    引擎基于每个线程的情况下使用。然而,自定义范围实际上可以做任何事情,这只是一个例子


  • 所以您需要一个实例池。EJB在您的环境中可用吗?
    @Stateless
    可能适合。@BalusC我正在使用Quarkus,它只支持CDI而不支持EJB。这里有一个CDI扩展,它提供了
    @pool
    注释:将它放在
    EngineManager
    上,然后将它注入到需要的其他地方。
    @ApplicationScoped
    有什么并发问题?我知道它不会解决您的问题,因为毕竟它的行为和singleton一样,但我仍然想知道有什么问题,因为它应该是线程安全的。并发问题是引擎对象不能并发调用,如果我的EngineManager是@ApplicationScope,多个线程可能同时访问引擎对象。