Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a ScopedProxy decide what Session to use?

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.

  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?
like image 501
Grim Avatar asked Oct 09 '15 12:10

Grim


People also ask

What is scoped proxy mode?

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).

What is the purpose of the session scope?

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.

Which is used to create a proxy of a scoped bean allowing that bean to participate in DI?

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.

What is global session?

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.


2 Answers

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

like image 75
Renat Gilmanov Avatar answered Nov 12 '22 16:11

Renat Gilmanov


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  
like image 27
question_maven_com Avatar answered Nov 12 '22 16:11

question_maven_com