14

我现在尝试了很多东西,但我似乎错过了一块拼图。这是故事:我有一个请求范围的 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 并且在请求线程等待结果时,我有其中三个并行计算的特性。我想要做的是提升/移交/传播bean或请求,使其可用于从原始http线程产生的子线程。特别是我想从异步提供的 CompletableFuture 中的 HttpServletRequest 中获取 SessionContext。

我尝试的是这个(替换get的实现):

final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
request.getAttribute(Constants.SESSION_CONTEXT_IDENTIFIER);

但这显然与请求范围的 bean 具有相同的结果。好吧,“getRequest”返回 null 而不是抛出异常。

作为第三种方法,我尝试了这个原始帖子

ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope();

cbf.registerScope("simpleThreadScope", simpleThreadScope);

我将 SessionContextProviderImpl 的范围设置为“simpleThreadScope”。不幸的是,这也不起作用,并引发了一个异常,即它在请求范围之外使用。

我正在使用的环境:泽西连同弹簧注射。

也许有人有一些想法?

问候

4

2 回答 2

16

对于任何未来的冒险家:

我花了一些时间挖掘 Spring 代码,发现RequestContextHolder具有可继承的RequestAttributesHolder。如果您查看有关内容的文档(继承自:InheritableThreadLocal),则可以阅读以下内容:

当在变量中维护的每个线程属性(例如,用户 ID、事务 ID)必须自动传输到创建的任何子线程时,可继承的线程局部变量优先于普通线程局部变量使用。

所以RequestContextHolder有一个字段,实际上setRequestAttributes 支持一个标志来使用inheritableRequestAttributesHolder。此外,如果您查看RequestContextListener -> 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 。

于 2016-06-07T14:44:58.697 回答
3

在我的情况下,使用OrderedRequestContextFilter解决了这个问题。您还必须像这样将 threadContextInheritable 标志设置为 true :

@Bean
public RequestContextFilter requestContextFilter() {
    OrderedRequestContextFilter filter = new OrderedRequestContextFilter();
    filter.setThreadContextInheritable(true);
    return filter;
}
于 2019-01-31T10:33:13.823 回答