@将@ Stateless-bean注入@ Singleton / @ ApplicationScoped-bean

时间:2019-07-30 09:11:25

标签: jpa java-ee thread-safety ejb

我将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"));
    }
}

我有以下声明:

“如果我将@Stateless-bean注入任何类型的@Singleton@ApplicationScoped-bean中,那么对于每次调用persistUser() ,容器池将提供MyStateless的另一个(不一定相同)实例,因此@Stateless-bean与@Singleton / {{1之间不存在1:1关系}}-bean,这也意味着,容器@ApplicationScoped被注入到我的entiyManager-bean中,该容器由我的@Stateless / @Singleton-bean通过{ {1}}是线程安全的。”

上述声明是否对所有3个版本正确,或者它们与我的无状态bean的行为是否不同?

并且请查看以下特殊情况(版本4):

@ApplicationScoped

第4版线程安全吗?必须这样做,因为我使用的是persistUser()的{​​{1}} Bean中的方法,并且按照定义,是由容器管理的@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); } } } stateless Bean中的变量必须是线程安全的。我说的对吗?

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

entityManager

但每个踏步的委托是不同的(委托给基础持久性提供程序休眠):

stateless

entityManager proxy 在每个线程上都是相同的,但是每个线程上的emProxy = org.jboss.as.jpa.container.TransactionScopedEntityManager@3297c304 -proxy的 delegate 是不同的,因此,它是线程安全的。我对吗?还是每个线程都需要不同的emDelegate = SessionImpl(2028358710<open>) 代理才能确保线程安全?

顺便说一句,如果我将entityManager更改为entityManager

,没有任何区别。
entityManager

如果我使用MyStateless,则所有同时运行的线程都具有相等的@ApplicationScoped-代理,但是具有不同的@ApplicationScoped public class MyStateless {..} -代理。因此,我看不出有任何理由选择@ApplicationScoped而不是entityManager

结论:

在上述情况下,没有问题:

“只要代理不同,相同的代理就可以用于同时运行的线程。”这些代理是什么(entityManager,无状态Bean等,都没有关系。)

1 个答案:

答案 0 :(得分:1)

根据JPA规范(2.1版,第7.2章):

  

不得在多个同时执行的线程之间共享实体管理器,因为实体管理器和持久性上下文不需要是线程安全的。实体管理器只能以单线程方式访问。

MyStateless bean可以被许多客户端使用,因为应用服务器负责注入的EntityManager。因此MyStateless对外部世界是线程安全的;从容器内手动产生的线程(即未使用应用服务器的工具(如EntityManager创建的线程)访问其ManagedExecutorService是不安全的。我什至不能确定,即使从容器管理的线程使用相同的EntityManager也是安全的。从多个客户端对应的线程并发使用它当然是安全的。另请参见EJB规范(版本3.2,第4.8.5章):

  

在单例会话bean实例状态中存储不支持并发访问(例如,对Java Persistence实体管理器或有状态会话bean的引用)的Java EE对象是合法的。但是,Bean Provider的责任是确保一次不能由多个线程访问此类对象。

请记住,只要手动生成的线程中没有发生访问MyStateless的版本1、2、3即可。仅因为(a)线程是由容器管理的,所以版本4可以保持原样,因为它们将确保每个容器使用不同的EntityManager和(b)工作负载每个线程的线程都是独立的-可以在不同的事务中运行。

实际上,注入“客户端”与注入的MyStateless Bean之间没有1-1的关系。实际上,容器应该只为每个注入点注入一个代理,并且代理负责解析要使用的适当实例。