问题描述
我现在尝试了很多事情,但是我似乎错过了一个难题.这是一个故事:我有一个请求范围的Bean,它从HttpServletRequest读取一些SessionContext.此属性在过滤器中设置.因此,当代码在正确的线程上运行时,这绝对可以正常工作.
I tried a lot of things now but i seem to miss a piece of the puzzle. Here is the story: I have a request scoped bean that reads some SessionContext from the HttpServletRequest. This attribute is set in a filter. So this is working absolutely fine while the code runs on the correct thread.
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.INTERFACES)
public class SessionContextProviderImpl implements SessionContextProvider<SessionContext> {
private final HttpServletRequest _request;
@Autowired
public SessionContextProviderImpl(HttpServletRequest request) {
_request = request;
}
@Override
public SessionContext get() {
return (SessionContext) _request.getAttribute(Constants.SESSION_CONTEXT_IDENTIFIER);
}
}
现在,我开始使用Java 8s的新功能CompletableFuture,在请求线程等待结果的同时,我拥有其中三个功能并行计算内容.我想要做的是提升/移交/传播bean或请求,使其可以在从原始http线程产生的子线程上使用.特别是,我想从提供的异步CompletableFuture内部的HttpServletRequest中获取SessionContext.
Now I started using java 8s new feature CompletableFuture and i have three of those features computing stuff in parallel while the request thread waits for the result. What i want to do is to promote/hand over/propagate the bean or request in a way that it can be used on child threads that have been spawned from the original http thread. In particular I would like to get the SessionContext from the HttpServletRequest from inside an asynchronous supplied CompletableFuture.
我尝试的是这个(get的替换实现):
what i tried is this (replaced implementation of get):
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
request.getAttribute(Constants.SESSION_CONTEXT_IDENTIFIER);
但这显然具有与请求范围的bean相同的结果.好吧,"getRequest"返回null而不是引发异常.
But this has obviously the same result as the request scoped bean. Well "getRequest" returns null instead of an exception thrown.
作为第三种方法,我尝试了:
As a third approach I tried this original post:
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope();
cbf.registerScope("simpleThreadScope", simpleThreadScope);
然后将SessionContextProviderImpl的范围设置为"simpleThreadScope".不幸的是,这也不起作用,并且引发了一个例外,即它在请求范围之外使用.
And i set the scope of the SessionContextProviderImpl to be "simpleThreadScope". Unfortunately this did not work either and threw an exception that it is used outside of a request scope.
我正在使用的环境:泽西岛与春季注入.
The environment I am using: Jersey together with spring injection.
也许有人有主意吗?
致谢
推荐答案
对于任何未来的冒险家:
For any future adventurers:
我花了一些时间来研究Spring代码并发现 RequestContextHolder .如果您查看有关内容的文档(继承自: InheritableThreadLocal )可以阅读以下内容:
I took some time to dig through the Spring code and found RequestContextHolder that has a inheritableRequestAttributesHolder. If you look at the documentation of what that is (inheriting from: InheritableThreadLocal) one can read the following:
当必须将在变量中维护的每个线程属性(例如,用户ID,事务ID)自动传输到创建的任何子线程时,可继承线程局部变量优先于普通线程局部变量使用
Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.
因此 RequestContextHolder 有一个用于该字段的字段,实际上setRequestAttributes支持使用继承的标志.此外,如果您查看 RequestContextListener -> requestInitialized,您发现它的调用没有标记(= false).所以我最终要做的是:
So the RequestContextHolder has a field for that and actually setRequestAttributes supports a flag to use inheritableRequestAttributesHolder. Furthermore if you look at RequestContextListener -> requestInitialized you find that it is called without the flag (= false). So what I ended up doing is this:
public class InheritableRequestContextListener extends RequestContextListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
InheritableRequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
@Override
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes, true);
}
}
瞧,我可以在子线程中访问SessionContextProvider.
And voila, I can access SessionContextProvider in child threads.
这篇关于Spring将请求范围的Bean提升为子线程(HttpServletRequest)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!