@Asynchronous bean中的Java注入 [英] Java injection inside @Asynchronous bean
问题描述
我有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屋!