ScopedProxy如何决定要使用的会话?
问题内容:
Singleton无法自动装配SessionBean,而ScopedProxy可以。
假设在同一应用程序中同时有100个用户有一个有效的会话,那么ScopedProxy如何确定会话的含义?
我认为ScopedProxy不会选择任何随机会话,我认为这是无稽之谈。
- ScopedProxy如何决定要使用的会话?
- 如果0个用户拥有一个会话该怎么办?会
NullPointerException
发生吗? - @Async是不同于调用Request-Processing-Thread的线程,如何将HttpRequest-Context注入到Async任务中?
问题答案:
ThreadLocal几乎就是您要寻找的答案。
此类提供线程局部变量。这些变量与普通变量不同,因为每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。
Holder类以线程绑定的RequestAttributes对象的形式公开Web请求。如果将Inheritable标志设置为true,则该请求将被当前线程产生的任何子线程继承。
在课程中,您将看到以下内容:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
这是实际的setter(注意它是静态的):
/**
* 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
。
如果您有足够的好奇心,这里是ThreadLocal.get
实现(whic返回此线程局部变量的当前线程副本中的值):
/**
* 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
:
/**
* 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: No thread-bound request found
DispatcherServlet,RequestContextListener和RequestContextFilter都做完全相同的事情,即将HTTP请求对象绑定到为该请求提供服务的Thread。这使得在请求链和会话范围内的Bean可以在调用链的更下游使用。
@Async和请求属性注入
一般来说,没有直接的方法可以解决问题。如前所述,我们具有线程绑定的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();
}
这是一个问题:在以下线程中访问作用域代理Bean:
如您所见,此解决方案依赖于事实构造函数将在适当的上下文中执行,因此可以缓存适当的上下文并在以后注入它。
这是挂在会话作用域bean上的另一个非常有趣的主题@Async带注释的方法