我有 2 个使用注入来“传递”从中提取的 UserData 信息的 beanHttpRequest
。如果我删除@Asynchronous
从 WorkerBean 开始,然后它就可以工作了,WorkerBean 可以访问注入的 UserInfo。
但是如果我使用@Asynchronous
在 WorkerBean 上,然后注入停止工作。
如果必须异步,手动创建/传递 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
}
}
用户数据是RequestScoped
并且绑定到 http 请求上下文,这意味着它依赖于当前请求,因此依赖于当前执行线程。@Asynchronous
主要通过使用服务器的线程池来实现。另外,CDI 不会将上下文传播到另一个线程,至少对于会话和请求上下文而言是如此。在这种情况下,它会创建一个新的请求上下文,即 ejb-inspiration 请求上下文,它与 http-request-context 无关。因此,在不同的线程中,您丢失了所有 http 会话和 http 请求上下文数据。根据当前的 CDI 规范,没有办法解决这个问题。
我的工作包括放弃@Asynchronous
完全注释,并使用ManagedExecutionService
,它基于某些标准,并且对于我需要的某些数据,通过以下方式将一些上下文数据传播到线程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 中线程本地的一些注释。线程是
重用时,您必须确定您传播的上下文是
删除,否则在具有不同用户数据的不同会话中,您
将会得到混合的数据,并且这将是一个难以调试的场景。
如果您可以避免这种情况,并且只需通过UserData
作为方法参数,越好。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)