Java ScopedProxy如何决定使用哪个会话?

Java ScopedProxy如何决定使用哪个会话?,java,spring,singleton,session-scope,Java,Spring,Singleton,Session Scope,Singleton不能自动连接SessionBean,但ScopedProxy可以 假设100个用户在同一应用程序中同时拥有一个有效会话,ScopedProxy如何决定会话的含义 我不认为ScopedProxy选择了任何随机会话,我认为这是胡说八道 ScopedProxy如何决定使用哪个会话 如果0个用户有一个会话怎么办?是否会发生NullPointerException异常 @Async与调用请求处理线程是不同的线程如何将HttpRequest上下文注入异步任务 这就是你想要的答案 此类提供线

Singleton不能自动连接SessionBean,但ScopedProxy可以

假设100个用户在同一应用程序中同时拥有一个有效会话,ScopedProxy如何决定会话的含义

我不认为ScopedProxy选择了任何随机会话,我认为这是胡说八道

  • ScopedProxy如何决定使用哪个会话
  • 如果0个用户有一个会话怎么办?是否会发生
    NullPointerException
    异常
  • @Async与调用请求处理线程是不同的线程如何将HttpRequest上下文注入异步任务
  • 这就是你想要的答案

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

    春天来了

    Holder类以线程绑定的形式公开web请求 请求属性对象。该请求将由任何子级继承 如果设置了可继承标志,则当前线程生成的线程 这是真的

    您将看到以下内容:

    private静态最终线程本地请求属性文件夹=
    新的NamedThreadLocal(“请求属性”);
    
    这是实际的setter(注意它是静态的):

    /**
    *将给定的RequestAttributes绑定到当前线程。
    *@param属性要公开的RequestAttributes,
    *或{@code null}重置线程绑定上下文
    *@param inheritable是否将RequestAttributes公开为可继承属性
    *对于子线程(使用{@link InheritableThreadLocal})
    */
    公共静态void setRequestAttributes(RequestAttributes属性,布尔可继承){}
    
    因此,正如您所看到的,这里没有什么神奇之处,只有一个特定于线程的变量,由
    ThreadLocal
    提供

    如果你够好奇,这里有
    ThreadLocal.get
    implementation(它返回当前线程的线程局部变量副本中的值):

    如您所见,它只依赖于
    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()
    在映射中执行查找。我希望你现在能看到全部情况

    关于潜在的NullPointerException

    基本上,您只能在作用域处于活动状态时调用代理的方法,这意味着执行线程应该是一个servlet请求。因此,使用这种方法,任何异步作业、命令等都将失败

    我想说,这是
    ScopedProxy
    背后的一个相当大的问题。它确实透明地解决了一些问题(例如简化了调用链),但如果不遵守规则,您可能会得到
    java.lang.IllegalStateException:未找到线程绑定请求

    ()说:

    DispatcherServlet、RequestContextListener和RequestContextFilter全部 执行完全相同的操作,即将HTTP请求对象绑定到 为该请求提供服务的线程。这使得豆子是 请求和会话作用域在调用链的更深处可用

    您还可以检查以下问题:

    @异步和请求属性注入

    一般来说,没有直接的方法来解决这个问题。如前所示,我们有线程绑定的RequestAttributes

    潜在的解决方案是手动传递所需对象,并确保
    @Async
    背后的逻辑考虑到这一点

    一个更聪明的解决方案(建议的)是透明地这样做。为了简化阅读,我将复制代码,并将链接放在代码块下

    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();
    } 
    
    这是一个问题:

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

    这是另一个非常有趣的话题

    Singleton不能自动连接SessionBean,但ScopedProxy可以

    这种说法有点令人困惑。它应该改写为

    Spring无法将会话范围的bean注入到单例范围的bean中 除非前者被定义为(范围)代理

    换句话说,Spring将无法将非代理会话范围的bean注入到单例范围的bean中。它将成功地将代理会话范围的bean注入到单例范围的bean中

    假设100个用户在同一时间内有一个有效会话 应用程序,ScopedProxy如何决定会话的含义

    首先要澄清的是,会话是Servlet容器的一个组件,由。Spring(和springmvc)使用会话范围的bean(以及其他东西,比如flash属性)来抽象它

    HttpSession
    对象通常通过适当的cookie与用户关联。HTTP请求提供具有用户标识值的cookie,Servlet容器检索或创建关联的
    HttpSession
    。换句话说,会话可以从请求中的信息中识别。您或Spring需要访问该请求

    Spring MVC显然可以通过
    DispatcherServlet
    访问请求,即使它通常不会向处理程序方法公开请求(请记住Spring MVC试图对您隐藏Servlet API)

    以下或多或少是一个实现细节。SpringMVC不会将请求对象(
    HttpServletRequest
    )一直传播到调用堆栈上,而是将其存储在

    Holder类以线程绑定的形式公开web请求
    RequestAttributes
    对象

    它可以做到这一点,因为Servlet容器通常(即非异步)在单个线程中处理请求。如果您正在该请求处理程序线程中执行代码,则可以访问该请求。如果是的话
    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();
    } 
    
    interface SessionScopedBean {...}
    class SessionScopedBeanImpl implements SessionScopedBean {...}
    
    SessionScopedBean proxy = (SessionScopedBean) Proxy.newProxyInstance(Sample.class.getClassLoader(),
            new Class<?>[] { SessionScopedBean.class }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    HttpSession session = ...;// get session through RequestContextHolder
                    SessionScopedBean actual = session.getAttribute("some.bean.identifier");
                    if (actual == null) {
                        // if absent, set it
                        session.setAttribute("some.bean.identifier", actual = new SessionScopedBeanImpl());
                    }
                    return method.invoke(actual, args); // delegate to actual object
                }
            });
    
    @Component
    @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
    class YourScopedProxy {
    
    public String dosomething() {
            return "Hello"; 
         }
    
    }
    
    
    @Component
    class YourSingleton {
     @Autowired private YourScopedProxy meScopedProxy;
    
     public String usedosomething(){
      return this.meScopedProxy.dosomething();
     }
    }
    
    
       1. How does the ScopedProxy decide what session to use?
    
    we have 100 users
    
    1 user (http session) call YourSingleton.usedosomething => call meScopedProxy :  
    => meScopedProxy  is not the YourScopedProxy  (original)  but a proxy to the YourScopedProxy
       and this proxy understands the scope 
    =>   in this case : proxy get real 'YourScopedProxy' object from  HTTP Session  
    
    
       2. What if 0 users have a Session? Will a NullPointerException occur?
    No because meScopedProxy is a proxy , when u use it 
    => proxy get real 'YourScopedProxy' object from  HTTP Session