我正在尝试在GlassFish 3上的Java EE JPA应用程序中实现某种审计。
我在@EntityListeners
实体上添加了@MappedSuperclass
注释,侦听器在其方法上有@PrePersist
和@PreUpdate
注释,这些注释在运行时很愉快地调用。
在这些方法中,我尝试使用(@Inject
)@Named
,@Stateful
,@SessionScoped
bean(UserSession
)以获取当前用户的ID。监听器类根本没有注释。
问题是我无法注入UserSession
bean;我总是以null
值结束。到目前为止,我尝试了 plain @Inject UserSession us;
,它总是注入一个空值。我也试过UserSession us = (UserSession) ctx.lookup("java:global/application/module/UserSession");
总是返回一个新对象(我验证了构造函数调用,加上对象为空)。
我很确定我错过了一些关于CDI非常重要的东西,但我无法弄清楚是什么。有人可以指出我正确的方向吗?
答案 0 :(得分:2)
EntityListners不支持CDI,至少在JPA 2.0中是这样。它显然位于JPA 2.1
中的新内容列表中当我碰到这个时,我也很惊讶。
答案 1 :(得分:2)
到这个时候,我尝试了普通的@Inject UserSession us;它总是注入一个空值。
这是因为在JPA 2.0中,类不是由CDI管理的,因此@Inject将无法使用它们。如Steve K所述,这些课程由CDI从JPA 2.1开始管理。
我也试过UserSession us =(UserSession)ctx.lookup(“java:global / application / module / UserSession”);
您无法在JNDI中查找由CDI实例化的bean。但是你可以做的是在JNDI中查找CDI BeanManager并从BeanManager中获取bean。 CDI规范保证您始终可以在“java:comp / BeanManager”中找到应用程序中的BeanManager。 这是一个简化的例子:
InitialContext ctx = new InitialContext();
BeanManager bm = ctx.lookup("java:comp/BeanManager");
Set<Bean<?>> beans = bm.getBeans(UserSession.class);
Bean<?> bean = bm.resolve(beans);
CreationalContext<?> ctx = bm.createCreationalContext(bean);
UserSession us = (UserSession) bm.getReference(bean, UserSession.class, ctx);
这将是由CDI管理的Bean,以防会话作用域为UserSession实例。
中提出的建议答案 2 :(得分:1)
我最终找到了一个解决方法,它允许我获取@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 。如果我创造了可用的东西,我会回复...