如何捕获org.jboss.weld.context.ContextNotActiveException异常?
解决JPA EntityListener远程EJB调用时无法捕获ContextNotActiveException的问题
嘿,我之前在JBoss EAP环境里碰到过类似的CDI上下文问题,先帮你分析下为什么原来的try-catch没生效,再给几个可行的解决方案:
为什么你的catch块捕获不到异常?
你注入的Credentials是CDI代理对象,当你调用credentials.getUserName()时,实际是代理类去尝试获取当前激活的会话上下文,这个过程中抛出的ContextNotActiveException可能被CDI容器的拦截器或代理逻辑处理了,没有直接冒泡到你的业务代码try-catch块里。所以即使它是RuntimeException的子类,你的catch也抓不到。
解决方案一:用Instance延迟获取实例(推荐)
改用CDI的Instance接口来注入,这样你可以主动控制获取Credentials实例的时机,确保异常能进入你的try-catch范围:
import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; import javax.persistence.PrePersist; import java.util.Date; public class CreateListener { @Inject @Named("credentials") private Instance<Credentials> credentialsInstance; @PrePersist public void setCreateAttributes(Auditable entity){ try { Credentials credentials = credentialsInstance.get(); entity.setCreateUserName(credentials.getUserName()); } catch (javax.enterprise.context.ContextNotActiveException | RuntimeException e) { entity.setCreateUserName("RemoteEJB"); } entity.setCreateTmstmp(new Date()); } }
Instance.get()方法会在上下文未激活时直接抛出ContextNotActiveException,这时候就能被你的catch块捕获到了,比直接捕获泛化的RuntimeException更精准。
解决方案二:用ThreadLocal传递审计信息(适合远程调用场景)
如果远程EJB调用是固定的场景,可以在远程调用入口处提前设置审计用户信息,用ThreadLocal在调用链中传递,避免依赖CDI上下文:
- 先创建一个全局的审计上下文类:
public class AuditContext { private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>(); public static void setCurrentUser(String user) { CURRENT_USER.set(user); } public static String getCurrentUser() { return CURRENT_USER.get(); } public static void clear() { CURRENT_USER.remove(); } }
- 在远程EJB的业务方法中设置用户信息(记得finally清理):
import javax.ejb.Stateless; @Stateless public class RemoteBusinessBean implements RemoteBusinessInterface { @Override public void performRemoteOperation() { try { AuditContext.setCurrentUser("RemoteEJB"); // 执行需要持久化的业务逻辑 } finally { AuditContext.clear(); } } }
- 修改EntityListener优先从ThreadLocal获取信息:
@PrePersist public void setCreateAttributes(Auditable entity){ String userName = AuditContext.getCurrentUser(); if (userName == null) { // 本地调用场景,尝试从Credentials获取 try { userName = credentials.getUserName(); } catch (RuntimeException e) { userName = "RemoteEJB"; } } entity.setCreateUserName(userName); entity.setCreateTmstmp(new Date()); }
这种方式完全不依赖CDI上下文,远程调用时主动注入信息,本地调用时 fallback 到原有逻辑,兼容性更好。
解决方案三:检查JBoss EAP远程调用上下文传递配置(可选)
如果你希望远程调用时也能传递会话上下文,可以检查JBoss EAP的配置:
- 确保远程EJB客户端配置了传递上下文的参数(比如
org.jboss.ejb.client.context.set.global=true) - 但注意:远程调用传递会话上下文可能带来安全性和状态管理的问题,一般不推荐无状态远程调用使用这种方式。
内容的提问来源于stack exchange,提问作者Avinash Moram
相关产品推荐
相关产品推荐

