Spring将请求范围的bean提升到子线程(HttpServletRequest) [英] Spring promoting request scoped bean to child threads (HttpServletRequest)

查看:32
本文介绍了Spring将请求范围的bean提升到子线程(HttpServletRequest)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在尝试了很多东西,但我似乎错过了一块拼图.这是故事:我有一个请求范围的 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.

我使用的环境:Jersey 和 spring 注入.

The environment I am using: Jersey together with spring injection.

也许有人有什么想法?

问候

推荐答案

对于任何未来的冒险者:

For any future adventurers:

我花了一些时间挖掘 Spring 代码,发现 RequestContextHolder 具有可继承的RequestAttributesHolder.如果您查看文档(继承自: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 支持使用inheritableRequestAttributesHolder 的标志.此外,如果您查看 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)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆