2015-10-09 17 views
11

Un Singleton non può autorizzare un SessionBean ma può farlo con ScopedProxy.In che modo ScopedProxy decide quale sessione utilizzare?

Supponendo che 100 utenti abbiano una sessione valida alla stessa ora nella stessa applicazione, in che modo ScopedProxy decide quale sessione si intende?

Non penso che ScopedProxy stia scegliendo una sessione casuale, questo sarebbe senza senso a mio avviso.

  1. In che modo ScopedProxy decide quale sessione utilizzare?
  2. Cosa succede se 0 utenti hanno una sessione? Si verificherà un NullPointerException?
  3. Un @Async è un thread diverso da invocando Request-Processing-Thread come iniettare HttpRequest-Context sull'attività Async?
+0

Cari downvoter, potresti spiegare che cosa è male appartiene alla domanda? Potrei riscriverlo in una forma più adatta. –

risposta

9

ThreadLocal è praticamente la risposta che stai cercando.

Questa classe fornisce variabili locali del thread. Queste variabili differiscono da (tramite il metodo get o set) della corrispondente variabile dalle rispettive controparti normali.

Spring trovi RequestContextHolder

classe Holder per esporre richiesta Web sotto forma di un filo-bound RequestAttributes oppongono. La richiesta verrà ereditata da qualsiasi sottoprocesso figlio generato dal thread corrente se il flag ereditabile è impostato su su true.

Inside the class vedrete il seguente:

private static final ThreadLocal<RequestAttributes> requestAttributesHolder = 
      new NamedThreadLocal<RequestAttributes>("Request attributes"); 

E qui è il setter reale (nota è statico):

/** 
    * 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) {} 

Quindi, come si può vedere, nessuna magia lì, solo una variabile specifica del thread, fornita da ThreadLocal.

Se siete curiosi abbastanza qui è ThreadLocal.get implementazione (whic restituisce il valore nella copia del thread corrente di questa variabile thread-local):

/** 
* 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(); 
} 

Come si può vedere si basa semplicemente sulla 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() esegue una ricerca all'interno della mappa. Spero che tu veda l'intera immagine ora.

Per quanto riguarda il potenziale NullPointerException

In sostanza, è possibile chiamare i metodi del proxy solo se il campo di applicazione è attiva, il che significa che l'esecuzione di thread deve essere una richiesta servlet.Quindi qualsiasi lavoro asincrono, comandi, ecc fallirà con questo approccio.

Direi, questo è un grosso problema dietro ScopedProxy. Lo fa risolvere alcuni problemi in modo trasparente (semplifica catena di chiamate, per examaple), ma se non si seguono le regole probabilmente otterrete java.lang.IllegalStateException: No thread-bound request found

(Spring Framework Reference Documentation) dice quanto segue:

DispatcherServlet, RequestContextListener e RequestContextFilter all fanno esattamente la stessa cosa, ovvero legano l'oggetto richiesta HTTP al thread che sta servendo quella richiesta. Questo rende i bean che sono richiesti e con ambito di sessione disponibili più in basso nella catena di chiamate.

è anche possibile controllare la seguente questione: Accessing request scoped beans in a multi-threaded web application

@Async e la richiesta di attributi iniezione

In generale, non v'è alcun modo semplice per risolvere il problema. Come mostrato in precedenza, abbiamo RequestAttributes con associazione a thread.

La soluzione potenziale è passare manualmente l'oggetto richiesto e assicurarsi che la logica sottostante @Async tenga conto di ciò.

Una soluzione un po 'più intelligente (suggerita da Eugene Kuleshov) è quella di farlo in modo trasparente. Copierò il codice per semplificare la lettura e inserire il link sotto il blocco di codice.

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(); 
} 

qui è che domanda: Accessing scoped proxy beans within Threads of

Come si può vedere, questa soluzione si basa sul fatto costruttore verrà eseguito nel giusto contesto, per cui è possibile memorizzare nella cache giusto contesto e iniettare in un secondo momento.

Ecco un altro, piuttosto interessante, argomento @Async annotated method hanging on session-scoped bean

+0

+1 per l'esempio ThreadLocal. Cerco sempre di descrivere gli ambiti usando gli esempi di sessione threadlocal e http. – HRgiger

+0

@PeterRader, vedere aggiornato. –

2

Un Singleton non può autowire un SessionBean ma uno ScopedProxy può.

Questa affermazione è un po 'di confusione. Dovrebbe essere riformulato come

di Primavera non può iniettare session bean con scope in fagioli Singleton con ambito a meno che i primi sono definiti come (ambito) proxy.

In altre parole, Spring non riesce a iniettare un bean con ambito sessione non proxy in un bean con ambito singleton. Riuscirà a iniettare un bean con scope di sessione proxy in un bean con scope singleton.

Assumendo 100 utenti hanno una sessione valida allo stesso tempo nello stesso applicazione, come fa lo ScopedProxy decidere cosa sessione si intende?

La prima cosa da chiarire è che una sessioneè un componente di un servlet container, rappresentato da un HttpSession. Spring (e Spring MVC) lo astraggono con i bean con scope di sessione (e altre cose come gli attributi flash).

HttpSession Gli oggetti sono in genere associati a un utente tramite i cookie appropriati. La richiesta HTTP fornisce un cookie con un valore di identificazione dell'utente e il contenitore Servlet recupera o crea un associato HttpSession. In altre parole, una sessione è identificabile dalle informazioni nella richiesta. Tu o Spring hai bisogno di accedere alla richiesta.

Spring MVC ha ovviamente accesso alla richiesta tramite lo DispatcherServlet, anche se in genere non lo espone ai metodi del gestore (ricorda che Spring MVC tenta di nascondere l'API Servlet da te).

Quanto segue è più o meno un dettaglio di implementazione. Spring MVC, invece di propagare l'oggetto della richiesta (HttpServletRequest) fino in fondo al callstack, lo memorizzerà in un RequestContextHolder.

classe Holder per esporre richiesta web nella forma di un RequestAttributes oggetto filo-bound.

Può farlo perché i contenitori Servlet in genere (ad esempio, non asincrono) gestiscono le richieste in un singolo thread. Se stai eseguendo il codice in quel thread del gestore richieste, hai accesso alla richiesta. E se hai accesso alla richiesta, you have access to the HttpSession.

L'implementazione effettiva è piuttosto lunga. Se vuoi approfondire, inizia con SessionScope e vai verso l'uscita.

Tutto ciò per dire che Spring non inietta un oggetto del tipo di bean concreto, inietta un proxy. L'esempio seguente, che utilizza JDK proxies (solo interfacce), è l'aspetto del comportamento dei proxy con scope della sessione. Dato

interface SessionScopedBean {...} 
class SessionScopedBeanImpl implements SessionScopedBean {...} 

primavera creerebbe un proxy SessionScopedBean simile (ma molto più sofisticato) per

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 
      } 
     }); 

e iniettare questo oggetto proxy nelle vostre fagioli Singleton (presumibilmente il controller). Quando il bean singleton sta per utilizzare il bean con ambito sessione, sta effettivamente passando attraverso il proxy e il proxy sta recuperando e delegando la chiamata a un oggetto reale.

Cosa succede se 0 utenti hanno una sessione? Si verificherà un NullPointerException?

La molla inietta il proxy. Il proxy non è null. È un oggetto. Sa come recuperare e utilizzare il target attuale. Con l'ambito della sessione, se un obiettivo non esiste, viene creato e salvato nella sessione (e quindi utilizzato).


Il pericolo qui sta tentando di utilizzare i proxy di sessione fuori dal contesto di una sessione. Come affermato in precedenza, l'intero trucco funziona perché i contenitori Servlet funzionano gestendo una singola richiesta all'interno di un singolo thread.Se si tenta di accedere a un bean con ambito sessione in un thread in cui una richiesta non è vincolata, verrà generata un'eccezione.

Come tale, non provare a passare i bean con scope di sessione sui limiti dei thread. Le specifiche Servlet consentono di utilizzare Async Processing e Spring MVC supports it con DefferedResult e Callable. C'è una serie di blog su di esso, here. Non è ancora possibile passare il bean con ambito sessione. Tuttavia, se si dispone di un riferimento allo AsyncContext, è possibile recuperare lo HttpServletRequest e accedere direttamente allo HttpSession.

Se si controlla come si inviano i thread (o meglio lo Runnable s), ci sono tecniche che è possibile utilizzare per copiare il contesto della richiesta, like the one described here.


Ecco alcuni messaggi relativi sugli ambiti e proxy (sessione):

+0

@PeterRader Modificato, ma per favore non iniziare ad aggiungere sempre più domande. Se hai nuove domande, fai una nuova domanda. –

2

farò una spiegazione molto semplice

@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