Jakarta ee NPE将单例bean注入单例bean

Jakarta ee NPE将单例bean注入单例bean,jakarta-ee,dependency-injection,ejb,cdi,Jakarta Ee,Dependency Injection,Ejb,Cdi,我遇到了一个奇怪的问题,在将一个单例bean(类B)注入另一个单例bean(类a)时,我得到了一个NullPointerException,其中两个bean都使用EJB3.1注释。发生NPE是因为即使注入了类B,从类A访问时映射也是空的。 例如: import javax.annotation.PostConstruct; 导入javax.ejb.Singleton; 导入javax.ejb.Startup; 导入javax.inject.inject; @独生子女 @启动 公共A类{ @注入

我遇到了一个奇怪的问题,在将一个单例bean(类B)注入另一个单例bean(类a)时,我得到了一个NullPointerException,其中两个bean都使用EJB3.1注释。发生NPE是因为即使注入了类B,从类A访问时映射也是空的。 例如:

import javax.annotation.PostConstruct;
导入javax.ejb.Singleton;
导入javax.ejb.Startup;
导入javax.inject.inject;
@独生子女
@启动
公共A类{
@注入
B B;
@施工后
公开无效初始化(){
b、 地图放置(“测试”,1);
System.out.println(“已初始化”);
}
}
import javax.annotation.PostConstruct;
导入javax.ejb.Singleton;
导入java.util.HashMap;
@独生子女
公共B级{
HashMap图;
@施工后
公开无效初始化(){
map=新的HashMap();
系统输出打印项次(“B初始化”);
}
}
但是,如果我对类B使用CDI singleton注释,代码将按预期执行:

import javax.annotation.PostConstruct;
导入javax.inject.Singleton;
导入java.util.HashMap;
@独生子女
公共B级{
HashMap图;
@施工后
公开无效初始化(){
map=新的HashMap();
系统输出打印项次(“B初始化”);
}
}

EJB注释在这种情况下不起作用有什么原因吗?

在@EJB和@Inject之间存在差异。 不幸的是,CDI和EJB注释是相似的,但工作方式可能不同

在您的例子中,如果Bean B是CDIBean(用CDI单例注释注释),那么它正确地注入了CDI


如果使用@EJB而不是@Inject,我假设EJB注入会起作用——如果Bean B是用javax.EJB.Singleton注释的。

许多人没有意识到的一个重要事实是,EJB客户端在注入EJB的地方只看到EJB的公共方法的“视图”

您正试图直接访问其中一个实例变量:

    b.map.put("test", 1);
如果将委托方法添加到EJB
B

public Integer put(String key, Integer value) {
    return map.put(key, value);
}
并调用它:

@PostConstruct
public void initialise() {
    b.put("test", 1);
    System.out.println("A initialised");
}
然后你会得到

20:18:16823信息[stdout](服务器服务线程池--70)B已初始化
20:18:16824信息[stdout](服务器服务线程池--70)已初始化

正如所料

有关更多信息,请参见EJB规范的§3。您可以在这里找到以下声明:

该容器向客户端透明地提供会话对象的安全性、并发性、事务、到辅助存储的交换以及其他服务


如果您要成功地直接访问实例变量,那么您将绕过所有这些。

尝试使用类
B
的即时初始化-例如,使用
@Startup
。通过这种方式,我怀疑B可能还没有初始化(您只得到某种代理对象),并且您尝试过快地访问它。只是再次尝试@Startup以确定,但仍然抛出一个NPE。甚至在A中尝试了@DependsOn(“B”),但也没有成功。
@PostConstruct
public void initialise() {
    b.put("test", 1);
    System.out.println("A initialised");
}