A Singleton can not autowire a SessionBean but a ScopedProxy can.
Assuming 100 users have a valid Session at the same time in the same application, how does the ScopedProxy decide what session is meant?
I don't think the ScopedProxy is choosing any random session, this would be nonsense in my opinion.
NullPointerException
occur?Provides a smart proxy backed by a scoped bean, which can be injected into object instances (usually singletons) allowing the same reference to be held while delegating method invocations to the backing, scoped beans. Used with scoped beans (non-singleton and non-prototype).
Session scope – Spring creates an instance and keeps the instance in the server's memory for the full HTTP session. Spring links the instance in the context with the client's session. Application scope – The instance is unique in the app's context, and it's available as the app is running.
Spring - Injecting a Bean as a class based Proxy Object We need to inject a proxy object that exposes the same public interface as the original scoped object. Spring uses CGLIB to create the proxy object.
A global session scope is similar to a session scope. The only difference is that it will be used in a Portlet application. Global sessions can be used when we have an application that is built on the JSR-168, JSR-286, and JSR-362 portal specifications.
ThreadLocal is pretty much the answer you are looking for.
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 has RequestContextHolder
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");
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) {}
So, as you can see, no magic there, just a thread-specific variables, provided by ThreadLocal
.
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();
}
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()
performs a lookup within the Map. I hope you see the whole picture now.
Regarding potential NullPointerException
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.
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
(Spring Framework Reference Documentation) says the following:
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.
You can also check the following question: Accessing request scoped beans in a multi-threaded web application
@Async and request attributes injection
Generally speaking, there is no straightforward way to solve the problem. As shown earlier we have thread-bound RequestAttributes.
Potential solution is to pass required object manually and make sure the logic behind @Async
takes that into account.
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();
}
Here is that question: Accessing scoped proxy beans within Threads of
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.
Here is another, pretty interesting, topic @Async annotated method hanging on session-scoped bean
I will make a very simple explanation
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With