我现在尝试了很多东西,但我似乎错过了一块拼图。这是故事:我有一个请求范围的bean,它从HttpServletRequest读取一些SessionContext。此属性在过滤器中设置。因此,当代码在正确的线程上运行时,这非常正常。
@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,当请求线程等待结果时,我有三个并行计算内容的功能。我想要做的是以一种可以在从原始http线程生成的子线程上使用它的方式来提升/移交/传播bean或请求。特别是我想从异步提供的CompletableFuture中获取来自HttpServletRequest的SessionContext。
我试过的是这个(取代了get的实现):
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
request.getAttribute(Constants.SESSION_CONTEXT_IDENTIFIER);
但这显然与请求范围的bean相同。 “getRequest”返回null而不是抛出异常。
作为第三种方法,我尝试了这个original post:
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope();
cbf.registerScope("simpleThreadScope", simpleThreadScope);
我将SessionContextProviderImpl的范围设置为“simpleThreadScope”。不幸的是,这也没有用,并抛出了一个例外,它在请求范围之外使用。
我正在使用的环境:泽西和弹簧注射。
也许有人有一些想法?
问候
答案 0 :(得分:9)
对于任何未来的冒险家:
我花了一些时间来挖掘Spring代码,发现RequestContextHolder有一个inheritableRequestAttributesHolder。如果你查看那些文档(继承自:InheritableThreadLocal),可以阅读以下内容:
当在变量中维护的每个线程属性(例如,用户ID,事务ID)必须自动传输到创建的任何子线程时,优先使用可传递的线程局部变量优先于普通的线程局部变量
所以RequestContextHolder有一个字段,实际上setRequestAttributes支持一个标志来使用inheritableRequestAttributesHolder。此外,如果你看RequestContextListener - &gt; requestInitialized你发现它被调用而没有标志(= false)。所以我最终做的是:
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。
答案 1 :(得分:1)
就我而言,使用OrderedRequestContextFilter可解决此问题。您还必须像这样将threadContextInheritable标志设置为true:
@Bean
public RequestContextFilter requestContextFilter() {
OrderedRequestContextFilter filter = new OrderedRequestContextFilter();
filter.setThreadContextInheritable(true);
return filter;
}