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