我最终找到了一种解决方法,它可以让我获得@Stateful bean 的引用:
我创建了一个 @Named @Singleton @Startup bean SessionController,它包含一个本地 HashMap<String, UserSession> sessionMap 以及我的 @Stateful bean 的引用:
@Named
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class SessionController {
private HashMap<String, UserSession> sessionMap;
@PostConstruct
void init() {
sessionMap = new HashMap<String, UserSession>();
}
@PreDestroy
void terminate() {
for (UserSession us : sessionMap.values()) {
us.logoutCleanUp(); //This is annotated as @Remove
}
sessionMap.clear();
}
public void addSession(String sessionId, UserSession us) {
sessionMap.put(sessionId, us);
System.out.println("New Session added: " + sessionId);
}
public UserSession getCurrentUserSession() {
FacesContext context = FacesContext.getCurrentInstance();
String sessionId = ((HttpSession) context.getExternalContext().getSession(false)).getId();
return sessionMap.get(sessionId);
}
}
我从每个 bean 的 @PostConstruct 方法中添加引用:
public class UserSession implements Serializable {
@Inject SessionController sc;
...
@PostConstruct
void init() {
FacesContext context = FacesContext.getCurrentInstance();
String sessionId = ((HttpSession) context.getExternalContext().getSession(true)).getId();
sc.addSession(sessionId, this);
}
注意.getSession(true),这是必需的,因为会话可能尚未创建。还要注意this 是安全传递的,因为@PostConstruct 不是构造函数...
在所有这些之后,我可以像这样在我的 EntityListener(和任何其他地方)中获取引用:
SessionController sc = (SessionController) new InitialContext().lookup("java:module/SessionController");
UserSession us = sc.getCurrentUserSession();
或者像这样在 CDI bean 中
@Inject SessionController sc;
我看到的唯一缺点是这种方法仅适用于 Web 应用程序(FacesContext context = FacesContext.getCurrentInstance() 有意义)。我的一些 bean(最后是我的 EntityListeners)也通过 @javax.jws.WebService 暴露为 @Stateless bean。在这种情况下(实际上:没有),我的 Singleton 将无法工作(尚未测试),因为没有任何类型的 sessionId (确切地说,根本没有会话)。我将不得不为此使用一种解决方法,可能使用 bean 的 SessionContext 或发明某种可用的 sessionId。如果我创造了一些有用的东西,我会回帖......