@Asynchronous bean 中的 Java 注入(inject)

标签 java asynchronous ejb inject

我有 2 个 bean 使用注入(inject)来“传递”从 HttpRequest 中提取的 UserData 信息。如果我从 WorkerBean 中删除 @Asynchronous,那么它的所有工作和 WorkerBean 都可以访问注入(inject)的 UserInfo。 但是,如果我在 WorkerBean 上使用 @Asynchronous,则注入(inject)将停止工作。

如果必须异步,手动创建 UserInfo 或将其传递到 WorkerBean 的最佳方法是什么?

// resource class
@Stateless
class MainRs {
    @Context
    protected HttpServletRequest request;
    @Inject
    protected UserData userData;
    @EJB
    WorkerBean job;

    @Path("/batch/job1")
    public function startJob() {
      // call on worker bean
      job.execute();
    }
}

// user data extracted from HttpRequest
@RequestScoped
@Default
class UserData {
  private HttpServletRequest request;
  private String userId;

  @Inject
  public UserData(HttpServletRequest request) {
    super();
    this.request = request;
    userId = request.getHeader("userId");
  }
  public int getUserId() {
    return userId;
  }
}

@Stateless
@Asynchronous
class WorkerBean {
    private UserData userData;

    // inject userData rom caller bean
    @Inject
    public WorkerBean(UserData userData) {
      super();
      this.userData = userData;
    }

    public function execute() {
      String userId = userData.getUserId();
      // do something
    }
}

最佳答案

UserData 是 RequestScoped 并且绑定(bind)到 http 请求上下文,这意味着它依赖于当前请求,因此依赖于当前执行线程。 @Asynchronous 将主要通过使用服务器的线程池来实现。此外,CDI 不会将上下文传播到另一个线程,至少对于 session 和请求上下文而言。在这种情况下,它创建了一个新的请求上下文,一个 ejb-invocation 请求上下文,它与 http-request-context 无关。因此,在不同的线程中,您丢失了所有 http session 和 http 请求上下文数据。按照当前的 CDI 规范,没有办法解决它。

我的工作涉及完全放弃 @Asynchronous 注释,并使用 ManagedExecutionService,它基于某些标准,对于我需要的一些数据,传播一些上下文数据到线程,通过 ThreadLocal。因此:

@Stateless
public class AsynchronouseService {

   @Resource
   private ManagedExecutorService managedExecutorService;

   @EJB
   private AsynchronouseServiceDelegate asynchronousServiceDelegate;

   @Inject
   private ManagedContextData managedContextData;


   public void executeAsync(Runnable runnable) {

   managedExecutorService.submit(() ->  asynchronousServiceDelegate.execute(runnable, managedContextData));

   }

}

@Stateless
public class AsynchronouseServiceDelegate {

   @Inject
   private ManagedContextDataProvider managedContextDataProvider;

   public void execute(Runnable runnable, ManagedContextData managedContextData){

    try {

       managedContextDataProvider.setExecutionContextData(managedContextData)
    runnable.run();

    } finally {
       managedContextDataProvider.clearExecutionContextData();
    }

   }
}

```

@ApplicationScoped
public class ManagedContextDataProvider {

   private static final ThreadLocal<ManagedContextData> managedContextDataContext;

   @Inject
   private Instance<HttpSession> httpSession;

   @Produces
   @Depedent
   public ManagedContextData getManagedContextData() {
     firstNonNull(managedContextDataContext.get(), httpSession.get().getAttribute(context_data_key));
   }

  public void setExecutionContextData(ManagedContextData managedContextData) {
    managedContextDataContext.set(managedContextData);
  }

  public void clearExecutionContextData() {
    managedContextDataContext.remove();
  }

}

Some NOTE about threadlocals in managedexecutorservice. Threads are reused, you must be certain that the context you propagate are removed, otherwise on a different session with different userdata, you will get mixed-up data, and it will be a hard-to-debug scenario.

如果你能避免这种情况,并且只将 UserData 作为方法参数传递,那就更好了。

关于@Asynchronous bean 中的 Java 注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38888007/

相关文章:

java - 从服务返回同步消息,然后进行异步处理 - 担心挂起线程吗?

java - EJB 不是 "visible"给 EJB 管理器。不能使用 CDI 或 JNDI 来引用它

java - @PostConstruct 在简单的@Singleton @Startup bean 上调用了两次

java - Avro 解码给出 java.io.EOFException

java - 在eclipse中导入javax

python - 如何使用 asyncio 处理很长的任务列表(生成器)

javascript - JQuery 等待多个延迟解析

java - 是否可以向我没有源代码的类添加断点?

java - 自动生成的 Web 服务调用因解码错误而失败?

java - EJB3 中的动态资源注入(inject)?