ScopedProxy如何决定要使用的会话? [英] How does a ScopedProxy decide what Session to use?

查看:131
本文介绍了ScopedProxy如何决定要使用的会话?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Singleton无法自动装配SessionBean,而ScopedProxy可以.

A Singleton can not autowire a SessionBean but a ScopedProxy can.

假设在同一应用程序中同时有100个用户具有有效的会话,那么ScopedProxy如何确定会话的含义?

Assuming 100 users have a valid Session at the same time in the same application, how does the ScopedProxy decide what session is meant?

我认为ScopedProxy没有选择任何随机会话,我认为这是胡说八道.

I don't think the ScopedProxy is choosing any random session, this would be nonsense in my opinion.

  1. ScopedProxy如何决定要使用哪个会话?
  2. 如果0个用户有一个会话怎么办? NullPointerException会发生吗?
  3. @Async与调用Request-Processing-Thread的线程不同,如何将HttpRequest-Context注入到Async任务中?
  1. How does the ScopedProxy decide what session to use?
  2. What if 0 users have a Session? Will a NullPointerException occur?
  3. A @Async is a different Thread than the invoking Request-Processing-Thread how to inject the HttpRequest-Context to the Async task?

推荐答案

ThreadLocal is pretty much the answer you are looking for.

此类提供线程局部变量.这些变量不同 与正常情况相比,每个线程访问一个线程 (通过其get或set方法)具有自己的,独立初始化的 变量的副本.

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.

Spring具有 RequestContextHolder

Holder类以线程绑定的形式公开Web请求 RequestAttributes对象.该请求将被任何孩子继承 如果设置了继承标志,则由当前线程生成的线程 变为真实.

Holder class to expose the web request in the form of a thread-bound RequestAttributes object. The request will be inherited by any child threads spawned by the current thread if the inheritable flag is set to true.

Inside the class you'll see the following:

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
            new NamedThreadLocal<RequestAttributes>("Request attributes");

这是实际的setter(请注意,它是静态的):

And here is the actual setter (note it is static):

/**
     * Bind the given RequestAttributes to the current thread.
     * @param attributes the RequestAttributes to expose,
     * or {@code null} to reset the thread-bound context
     * @param inheritable whether to expose the RequestAttributes as inheritable
     * for child threads (using an {@link InheritableThreadLocal})
     */
    public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {}

因此,如您所见,那里没有魔术,只有ThreadLocal提供的特定于线程的变量.

So, as you can see, no magic there, just a thread-specific variables, provided by ThreadLocal.

如果您有足够的好奇心,这里是ThreadLocal.get实现(whic返回此线程局部变量的当前线程副本中的值):

If you are curios enough here is ThreadLocal.get implementation (whic returns the value in the current thread's copy of this thread-local variable):

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

如您所见,它仅依赖于ThreadLocalMap:

As you can see it simply relies on ThreadLocalMap:

/**
 * ThreadLocalMap is a customized hash map suitable only for
 * maintaining thread local values. No operations are exported
 * outside of the ThreadLocal class. The class is package private to
 * allow declaration of fields in class Thread.  To help deal with
 * very large and long-lived usages, the hash table entries use
 * WeakReferences for keys. However, since reference queues are not
 * used, stale entries are guaranteed to be removed only when
 * the table starts running out of space.
 */
static class ThreadLocalMap {

getEntry()在Map中执行查找.希望您现在能看到整个图片.

getEntry() performs a lookup within the Map. I hope you see the whole picture now.

关于潜在的NullPointerException

基本上,只有在作用域处于活动状态时,才可以调用代理的方法,这意味着执行线程应该是servlet请求.因此,使用此方法,所有异步作业,命令等都将失败.

Basically, you can call proxy's methods only if the scope is active, which means executing thread should be a servlet request. So any async jobs, Commands, etc will fail with this approach.

我会说,这是ScopedProxy背后的一个很大的问题.它确实透明地解决了一些问题(例如,简化了呼叫链),但是如果您不遵循规则,则可能会得到java.lang.IllegalStateException: No thread-bound request found

I would say, this is quite a big problem behind ScopedProxy. It does solve some issues transparently (simplifies call chain, for examaple), but if you don't follow the rules you'll probably get java.lang.IllegalStateException: No thread-bound request found

(

DispatcherServlet,RequestContextListener和RequestContextFilter全部 做完全相同的事情,即将HTTP请求对象绑定到 正在为该请求提供服务的线程.这使得豆是 请求范围和会话范围可在调用链的更下游使用.

DispatcherServlet, RequestContextListener and RequestContextFilter all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.

您还可以检查以下问题:访问在多线程Web应用程序中请求作用域Bean

You can also check the following question: Accessing request scoped beans in a multi-threaded web application

@异步和请求属性注入

通常来说,没有直接的方法可以解决问题.如前所示,我们有线程绑定的RequestAttributes.

Generally speaking, there is no straightforward way to solve the problem. As shown earlier we have thread-bound RequestAttributes.

可能的解决方案是手动传递所需的对象,并确保@Async背后的逻辑考虑在内.

Potential solution is to pass required object manually and make sure the logic behind @Async takes that into account.

更聪明的解决方案(由 Eugene Kuleshov 建议)是透明地执行此操作的.为了简化阅读,我将复制代码并将链接放在代码块下面.

A bit more clever solution (suggested by Eugene Kuleshov) is to do that transparently. I'll copy the code in order to simplify reading and put the link under the code block.

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

/**
 * @author Eugene Kuleshov
 */
public abstract class RequestAwareRunnable implements Runnable {
  private final RequestAttributes requestAttributes;
  private Thread thread;

  public RequestAwareRunnable() {
    this.requestAttributes = RequestContextHolder.getRequestAttributes();
    this.thread = Thread.currentThread();
  }

  public void run() {
    try {
      RequestContextHolder.setRequestAttributes(requestAttributes);
      onRun();
    } finally {
      if (Thread.currentThread() != thread) {
        RequestContextHolder.resetRequestAttributes();
      }
      thread = null;
    }
  }

  protected abstract void onRun();
} 

这里是一个问题:

如您所见,该解决方案依赖于事实构造器将在适当的上下文中执行,因此可以缓存适当的上下文并在以后注入它.

As you can see, this solution relies on the fact constructor will be executed in the proper context, so it is possible to cache proper context and inject it later.

这是另一个非常有趣的主题,挂在会话作用域bean上的@Async注释方法

Here is another, pretty interesting, topic @Async annotated method hanging on session-scoped bean

这篇关于ScopedProxy如何决定要使用的会话?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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