@Asynchronous bean中的Java注入 [英] Java injection inside @Asynchronous bean

查看:88
本文介绍了@Asynchronous bean中的Java注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have 2 beans that use Injection to "pass" UserData info that is extracted from HttpRequest. If I remove @Asynchronous from WorkerBean then its all working and WorkerBean can access UserInfo thats injected down. However if I use @Asynchronous on WorkerBean then injection stops working.

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

What is the best way to manually create/pass UserInfo into WorkerBean if it has to be asynchronous?

// 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 并绑定到http请求上下文,这意味着它依赖于当前请求,因此依赖于当前执行线程。 @Asynchronous 将会实现,主要是通过使用服务器的线程池来实现的。另外,CDI至少在会话和请求上下文中不会将上下文传播到另一个线程。在这种情况下,它将创建一个新的请求上下文,即ejb调用请求上下文,它与http-request-context没有任何关系。因此,在另一个线程中,您丢失了所有http会话和http请求上下文数据。

UserData is RequestScoped and bounded to the http request context, that implies that it is dependent on the current request, and hence on the current thread of execution. @Asynchronous would be implemented, mostly through the use of the server's thread-pool. Additional, CDI does not propagate contexts to another thread, at least for session and request context. In this case, it creates a new request-context, an ejb-invocation request context, which has nothing to do with the http-request-context. In a different thread therefore, you have lost all http session and http request context data. As at current CDI specs, there is no way around it.

我的工作涉及放弃 @Asynchronous 批注一起使用 ManagedExecutionService ,它基于某些条件以及我需要的某些数据,通过 ThreadLocal <传播一些上下文数据到线程/ code>。因此:

My work around involved ditching the @Asynchronous annotation altogether, and using ManagedExecutionService, which based on certain criteria's, and for some data that I require, propagates some contexts data to the thread, through ThreadLocal. Thus:

@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();
  }

}




有关Managedexecutorservice中的线程本地的一些说明。线程被重用
,必须确保传播的上下文已被删除
,否则在具有不同用户数据的不同会话中,
将获得混合数据,这将是一个

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 作为方法参数更好。

If you can avoid this scenario, and just pass through the UserData as method parameter, the better.

这篇关于@Asynchronous bean中的Java注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆