Jpa @将@Stateless bean注入@Singleton/@ApplicationScoped bean
我将Java EEJpa @将@Stateless bean注入@Singleton/@ApplicationScoped bean,jpa,jakarta-ee,thread-safety,ejb,Jpa,Jakarta Ee,Thread Safety,Ejb,我将Java EE@无状态-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.
@无状态
-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"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
第2版:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
第3版:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
我有以下声明:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
“如果我向任何类型的@Singleton
或@ApplicationScoped
-bean中注入一个@Stateless
-bean,那么对于persistUser()
的每次调用,容器池将提供另一个(不一定相同)MyStateless
的实例。因此,@Stateless
-bean和@Singleton
/@ApplicationScoped
-bean之间没有1:1的关系。这也意味着,容器管理的EntityManager
注入到我的@Stateless
-bean中,我的@Singleton
-bean间接使用它>/@ApplicationScoped
-bean通过方法persistUser()
是线程安全的
上述语句是否适用于所有3个版本,或者它们在我的无状态bean中的行为是否会有所不同?
请查看以下特殊情况(第4版):
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
但每个步骤的委托不同(委托给基础持久性提供程序hibernate):
如果我使用@ApplicationScoped
,那么所有同时运行的线程都具有相同的entityManager
-代理,但不同的entityManager
-代理。因此,我看不出有任何理由选择@Stateless
而不是@ApplicationScoped
结论:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
@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"));
}
}
@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);
}
}
}
上述情况不存在任何问题:
“相同的代理可以在同时运行的线程中使用,只要它们的代理不同。”这些代理是什么(entityManager、无状态Bean等并不重要。)来自JPA规范(版本2.1,第7.2章):
实体管理器不能在多个并发执行的线程之间共享,因为实体管理器和持久性上下文不需要是线程安全的。只能以单线程方式访问实体管理器
MyStateless
bean可以被许多客户机使用,因为应用服务器负责注入的EntityManager
。因此,MyStateless
对于外部世界是线程安全的;从容器中手动生成的线程访问其EntityManager
(即,不使用应用程序服务器的工具创建的线程,如ManagedExecutorService
)将是不安全的。我甚至不确定使用相同的EntityManager
,即使是从容器管理的线程,是否安全。当然,从对应于多个客户端的线程并发使用它是安全的。另请参见EJB规范(版本3.2,第4.8.5章):
在单例会话bean实例状态中存储不支持并发访问(例如,对Java持久性实体管理器或有状态会话bean的引用)的Java EE对象是合法的。但是,Bean提供者有责任确保一次不被多个线程访问这些对象
记住这一点,访问MyStateless的版本1、2、3是可以的,只要它们不出现在手动生成的线程中。版本4可以,因为这只是因为(a)线程是容器管理的,因此容器将确保在每个线程中使用不同的EntityManager
,(b)每个线程的工作负载是独立的-可以在不同的事务中运行
实际上,注入“客户机”和
MyStateless
注入bean之间没有1-1关系。实际上,容器应该只向每个注入点注入一个代理,代理负责解析要使用的适当实例。这是否意味着在@Singleton中执行的以下代码不是线程安全的?:@Resource private ManagedScheduledExecutor Service managedExecutor;。。。public void task(){managedExecutor.scheduleWithFixedDelay(()->stateless.persist(新用户(“hello”)}..,0,5,TimeUnit.SECONDS)代码>ManagedExecutorService中的无状态.persist(新用户(“hello”)
必须是每个定义的线程安全的,因为我使用的是无状态的-bean,在每个无状态的-bean中都有一个线程安全的entityManager
。因此,它总体上是线程安全的。我说得对吗?我不确定容器管理的线程会发生什么情况(正如我上面所写的)。显然,根据您的观察,容器管理的线程得到了一个正确的、单独的EntityManager
实例。如果每个线程的工作负载独立于其他线程,那么这很可能会起作用。但是EMs不同这一事实也意味着不同的事务,因此如果线程需要在相同的数据上进行协作,那么我认为EM应该被视为非线程安全的。代理只有一个职责:找到bean的适当的真实实例(您提到的委托)并调用它。所以不用担心,注入的“作用域代理”是同一个实例是绝对正常的。关于无状态
和@ApplicationScoped
的区别,有很多参考资料,我建议进行一些独立于此问题的搜索和阅读(因为我们正在慢慢偏离主题)。作为一个例子,我想到的一个区别是会话EJB和CDIBean如何处理事务(默认值:对于EJB为ON,对于CDI为OFF)。还有更微妙的差异,可能会也可能不会影响你的具体情况。