使用EJBContext getContextData - 这样安全吗?

wrs*_*der 12 java java-ee java-ee-6 ejb-3.1

我打算用来EJBContext将一些属性从应用程序层(特别是消息驱动的bean)传递给持久性生命周期回调,它不能直接注入或传递参数(EclipseLink中的会话监听器,实体生命周期回调等),并且该回调正在获得EJBContext通过JNDI.

这看起来有效,但有没有任何隐藏的陷阱,比如线程安全或对象寿命,我错过了?(假设传递的属性值是不可变的,如String或Long.)

示例bean代码

@MessageDriven
public class MDB implements MessageListener {
   private @Resource MessageDrivenContext context;

   public void onMessage(Message m) { 
      context.getContextData().put("property", "value");
   }
}
Run Code Online (Sandbox Code Playgroud)

然后是使用EJBContext的回调

public void callback() { 
   InitialContext ic = new InitialContext();
   EJBContext context = (EJBContext) ic.lookup("java:comp/EJBContext");
   String value = (String) context.getContextData().get("property");
} 
Run Code Online (Sandbox Code Playgroud)

我想知道的是,我能确定contextData地图内容只对当前的调用/线程可见吗?换句话说,如果两个线程同时运行该callback方法,并且都EJBContext从JNDI 查找,它们实际上是获得不同的contextData地图内容?

而且,这实际上是如何工作的 - EJBContext从JNDI查找返回的是ThreadLocal最终围绕类似结构的包装器对象?

Arj*_*jms 10

我认为一般来说,该方法的契约是启用拦截器+ webservice上下文和bean之间的通信.因此,只要没有创建新的调用上下文,上下文就应该可用于所有代码.因此,它应该绝对是线程安全的.

EJB 3.1规范的第12.6节说明如下:

InvocationContext对象提供的元数据使拦截器方法能够控制调用链的行为.上下文数据在单独的业务方法调用或生命周期回调事件中不可共享.如果由于在Web服务端点上调用而调用拦截器,则getContextData返回的映射将是JAX-WS MessageContext

此外,getContextData方法在4.3.3中描述:

getContextData方法使业务方法,生命周期回调方法或超时方法能够检索与其调用关联的任何拦截器/ webservices上下文.

在实际实现方面,JBoss AS执行以下操作:

public Map<String, Object> getContextData() {
    return CurrentInvocationContext.get().getContextData();
}
Run Code Online (Sandbox Code Playgroud)

CurrentInvocationContext使用基于上的一摞线程本地链表流行,推动当前调用上下文.

请参阅org.jboss.ejb3.context.CurrentInvocationContext.调用上下文只是懒惰地创建一个简单的HashMap,就像在org.jboss.ejb3.interceptor.InvocationContextImpl中一样.

Glassfish做了类似的事情.它还获得一个调用,并从调用管理器执行此操作,调用管理器还使用基于线程局部数组列表的堆栈来弹出并再次推送这些调用上下文.

GlassFish实现的JavaDoc在这里特别有趣:

此TLS变量存储ArrayList.ArrayList包含ComponentInvocation对象,这些对象表示此线程上的调用堆栈.对ArrayList的访问不需要同步,因为每个线程都有自己的ArrayList.

就像在JBoss AS中一样,GlassFish也懒得创建一个简单的东西HashMap,在本例中是com.sun.ejb.EjbInvocation.对GlassFish案例感兴趣的是,Web服务连接更容易在源中发现.


Jör*_*ann 8

我无法直接帮助您解决您的问题EJBContext,因为getContextData在JEE6中添加了该方法,但仍然没有太多关于它的文档.

但是,有另一种方法可以使用TransactionSynchronizationRegistry在EJB,拦截器和生命周期回调之间传递上下文数据.概念和示例代码可以在Adam Bien的博客文章中找到.

javax.transaction.TransactionSynchronizationRegistry拥有类似Map的结构,可用于在事务中传递状态.它从旧的J2EE 1.4天开始就完美运行,并且与线程无关.

因为Interceptor在与ServiceFacade相同的事务中执行,所以甚至可以在@AroundInvoke方法中设置状态.的TransactionSynchronizationRegistry(TSR)可被直接注入到拦截.

这个例子使用@Resource注入来获取TransactionSynchronizationRegistry,但也可以从InitialContext这样查找:

public static TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry() throws NamingException {
    InitialContext ic = new InitialContext();
    return (TransactionSynchronizationRegistry)ic.lookup("java:comp/TransactionSynchronizationRegistry");
}
Run Code Online (Sandbox Code Playgroud)

  • TransactionSynchronizationRegistry有一个限制:它总是需要一个事务,但在某些情况下,有必要在没有事务的情况下传播信息 (3认同)