5

我正在尝试在 GlassFish 3 上的 Java EE JPA (2.0) 应用程序中实现某种审计。

@EntityListeners在我的实体上添加了一个注释@MappedSuperclass,侦听器在其方法上有@PrePersistand注释,这些方法在运行时被愉快地调用。@PreUpdate

在这些方法中,我尝试使用 ( @Inject) a @Named, @Stateful, @SessionScopedbean ( UserSession) 来获取当前用户的 ID。侦听器类根本没有注释。

问题是我无法UserSession注入 bean;我总是以一个null值结束。到这一次,我尝试了总是注入空值的plain 。我也尝试了总是返回一个新对象(我验证了构造函数调用,加上对象是的)。@Inject UserSession us;UserSession us = (UserSession) ctx.lookup("java:global/application/module/UserSession");

我很确定我错过了关于 CDI 的一些非常重要的事情,但我不知道是什么。有人可以指出我正确的方向吗?

4

3 回答 3

2

EntityListner 不支持 CDI,至少在 JPA 2.0 中是这样。它显然在JPA 2.1中的新事物列表中

当我遇到这个时,我也很惊讶。

于 2012-07-06T19:54:35.570 回答
1

我最终找到了一种解决方法,它可以让我获得@Statefulbean 的引用:

我创建了一个@Named @Singleton @Startupbean SessionController,它包含一个HashMap<String, UserSession> sessionMap带有我的 bean 引用的本地@Stateful

@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)which 是必需的,因为 Session 可能尚未创建。另请注意,由于不是构造函数,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()有意义的地方)。@javax.jws.WebService我的一些 bean(最后是我的 EntityListeners)也通过@Statelessbean 公开。在这种情况下(实际上:没有),我的 Singleton 将无法工作(尚未测试),因为没有任何类型的sessionId(确切地说根本没有会话)。我将不得不为此使用一种解决方法,可能使用 bean 的 SessionContext 或发明某种可用的sessionId。如果我创造了一些有用的东西,我会回帖......

于 2012-07-07T15:05:09.617 回答
1

到这个时候,我尝试了普通的@Inject UserSession 我们;它总是注入一个空值。

这是因为在 JPA 2.0 中,这些类不是由 CDI 管理的,所以 @Inject 不能与它们一起使用。正如 Steve K. 所指出的,这些类从 JPA 2.1 开始由 CDI 管理。

我也试过 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 实例。

我的回答是基于EntityListeners 中的 CDI 注入中提出的内容

于 2013-10-09T12:19:34.880 回答