@Inject 一个@Stateless-bean 到@Singleton/@ApplicationScoped-bean

@Inject a @Stateless-bean into @Singleton/@ApplicationScoped-bean

我将 Java EE @Stateless-bean 注入 @Singleton/@ApplicationScoped-bean:

@Stateless
public class MyStateless {

    @PersistenceContext
    private EntityManager em;

    public void persist(User u){ 

        // for each thread the emProxy is the same
        log.info("emProxy={0}", em.toString());

        // for each thread the emDelegate is differently
        log.info("emDelegate={0}", em.getDelegate().toString());

        // this is definitly thread-safe !
        em.persist(u);

    }
}

版本 1:

@javax.ejb.Singleton
public class MySingleton{

    @Inject
    private MyStateless stateless;

    public void persistUser(){
        // is this thread safe?
        stateless.persist(new User("hello"));
    }
}

版本 2:

@ApplicationScoped
public class MySingleton{

    @Inject
    private MyStateless stateless;

    public void persistUser(){
        // is this thread safe?
        stateless.persist(new User("hello"));
    }
}

版本 3:

@javax.ejb.Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MySingleton{

    @Inject
    private MyStateless stateless;

    public void persistUser(){
        // is this hread safe ?
        stateless.persist(new User("hello"));
    }
}

我有以下声明:

"If I inject a @Stateless-bean into any kind of @Singleton or @ApplicationScoped-bean, then for each call of persistUser() the container pool will provide another (and not necessarily the same) instance of MyStateless. Thus, there is no 1:1-relation between @Stateless-bean and the @Singleton/ @ApplicationScoped-bean. That also means, the container managed entiyManager injected in my @Stateless-bean which is indirectly used by my @Singleton/@ApplicationScoped-bean through the method persistUser() is thread-safe."

以上声明是否适用于所有 3 个版本,或者它们对我的无状态 bean 的行为是否不同?

请看下面的特例(版本4):

@javax.ejb.Singleton
public class MySingleton {

    @Resource private ManagedScheduledExecutorService managedExecutor;

    @Inject
    private MyStateless stateless;

    public void persistUser(){
        // is this thread safe?
        for(int i=0; i<=4;i++){
            managedExecutor.scheduleWithFixedDelay(() 
            -> stateless.persist(new User("hello_" + i)) , 0, 5, TimeUnit.SECONDS);
        }
    }
}

版本 4 线程安全吗? 它必须,因为我使用 stateless-bean 中的方法作为 Runnable 并且根据定义容器管理entityManagerstateless-bean 中必须是线程安全的。我说得对吗?

查看日志,每个线程打印相同的 entityManager-proxy:

emProxy = org.jboss.as.jpa.container.TransactionScopedEntityManager@3297c304

但是每个 tread 的委托是不同的(委托给底层持久性提供程序休眠):

emDelegate = SessionImpl(2028358710<open>)

entityManagerproxy 在每个线程上都是相同的,但是 entityManagerdelegate - 每个线程上的代理是不同的,因此它是线程安全的。我对吗?或者我还需要不同的 entityManager-proxies 让每个线程成为线程安全的吗?

顺便说一句,如果我将 MyStateless 更改为 @ApplicationScoped

,则没有区别
@ApplicationScoped
public class MyStateless {..}

如果我使用 @ApplicationScoped,那么所有同时 运行 线程具有相等的 entityManager- 代理 但不同的 entityManager- 代表。所以我看不出有什么理由选择 @Stateless 而不是 @ApplicationScoped.

结论:

以上这些情况都没有问题:

"The same proxies can be used in simultanously running threads as long as their delegates are different." 这些代理是什么(entityManager、stateless beans 等无关紧要。)

来自 JPA 规范(2.1 版,第 7.2 章):

An entity manager must not be shared among multiple concurrently executing threads, as the entity manager and persistence context are not required to be threadsafe. Entity managers must only be accessed in a single-threaded manner.

MyStateless bean 可以被许多客户端使用,因为应用程序服务器负责处理注入的 EntityManager。所以MyStateless对外界是线程安全的;从容器内手动生成的线程(即不使用应用程序服务器的设施创建的线程,如 ManagedExecutorService)访问它的 EntityManager 是不安全的。我什至不确定使用相同的 EntityManager,即使是来自容器管理的线程,是否安全。从对应于多个客户端的线程并发使用它当然是安全的。另请参阅 EJB 规范(版本 3.2,第 4.8.5 章):

It is legal to store Java EE objects that do not support concurrent access (e.g. references to Java Persistence entity managers or stateful session beans) within the singleton session bean instance state. However, it is the responsibility of the Bean Provider to ensure such objects are not accessed by more than one thread at a time.

考虑到这一点,访问 MyStateless 的版本 1、2、3 是可以的,只要它们不出现在手动生成的线程中。版本 4 可以 因为它是 只是因为 (a) 线程是容器管理的,所以容器将确保在每个线程中使用不同的 EntityManager 和 (b ) 每个线程的工作负载都是独立的 - 可以 运行 在不同的事务中。

而且注入 "client" 和 MyStateless 注入的 bean 之间确实没有 1-1 关系。实际上,容器应该只向每个注入点注入一个代理,代理负责解析要使用的适当实例。