2015-10-09 13 views
11

シングルトンはSessionBeanをオートワイヤできませんが、ScopedProxyはオートウィークできません。ScopedProxyはどのセッションを使用するかをどのように決定するのですか?

100人のユーザーが同じアプリケーションで同時に有効なセッションを持つと仮定すると、ScopedProxyはどのようなセッションを意味するのでしょうか?

私はScopedProxyがランダムなセッションを選択しているとは思わないが、これは私の意見ではナンセンスだろう。

  1. ScopedProxyはどのセッションを使用するかをどのように決定しますか?
  2. 0人のユーザーにセッションがある場合はどうなりますか? NullPointerExceptionは発生しますか?
  3. @Asyncは、非同期タスクにHttpRequest-Contextを挿入する方法とは異なり、要求処理スレッドを呼び出すスレッドとは異なりますか?
+0

親愛なるdownvoter、あなたは悪いことを説明することができる質問に属していますか?私はそれをより適切な形に書き直すかもしれません。 –

答えて

9

ThreadLocalあなたが探している答えはかなりです。

このクラスはスレッドローカル変数を提供します。これらの変数は、 (getメソッドまたはsetメソッドを介して)にアクセスする各スレッドが、独自に独立して初期化された変数のコピーを持つという点で、通常のカウンタとは異なります。

スプリングは、スレッド結合 RequestAttributesオブジェクトの形でWebリクエストを露出するRequestContextHolder

ホルダークラスを有しています。継承可能フラグが に設定されている場合、現在のスレッドによって生成されたスレッド の子によって継承されます。

Inside the class次のことを見ないだろう:

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

そしてここでは、実際のセッター(それが静的であることに注意)である:あなたが見ることができるように、そう

/** 
    * 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)です:

を:あなたは、それは単に ThreadLocalMapに依存して見ることができるように

/** 
* 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 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が

について

、あなたはスレッドがサーブレット要求する必要があります実行を意味し、スコープがアクティブである場合にのみ、プロキシのメソッドを呼び出すことができます。したがって、このアプローチでは、非同期ジョブやコマンドなどは失敗します。

これは、ScopedProxyの背後にある大きな問題です。 (examapleため、呼び出しチェーンを簡素化)透過的にいくつかの問題を解決していますが、ルールに従わない場合、あなたはおそらく

Spring Framework Reference Documentationjava.lang.IllegalStateException: No thread-bound request foundを取得します言う以下:

のDispatcherServlet、RequestContextListener RequestContextFilter all は全く同じことを行います。つまり、HTTP要求オブジェクトを、その要求を処理している スレッドにバインドします。これにより、 のBeanがリクエストチェーンとセッションスコープの両方でコールチェーンを利用できるようになります。

また、次の質問をチェックすることができます:Accessing request scoped beans in a multi-threaded web application

@Asyncを、要求が一般的な問題を解決するための簡単な方法がない、話す注入

属性。前に示したように、スレッドにバインドされたRequestAttributesがあります。

潜在的な解決策は、必要なオブジェクトを手動で渡し、@Asyncの背後にある論理がそれを考慮に入れることです。

もう少し巧妙な解決策(Eugene Kuleshovが示唆)は、それを透過的に行うことです。私はコードをコピーして読み込みを簡略化し、コードブロックの下にリンクを張ります。ここで

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

はその質問です:あなたが見ることができるようにAccessing scoped proxy beans within Threads of

、この解決策は適切なコンテキストで実行されるという事実のコンストラクタに依存しているので、適切なコンテキストをキャッシュし、後でそれを注入することができます。ここで

はAシングルトンはSessionBeanのが、ScopedProxy缶をautowireすることはできません別の、かなり興味深い、話題@Async annotated method hanging on session-scoped bean

+0

ThreadLocalの例で+1。私はいつもthreadlocalとhttpセッションの例を使ってスコープを記述しようとします。 – HRgiger

+0

@PeterRader、更新された参照してください。 –

2

です。

この文は紛らわしいものです。前者は(スコープ)プロキシとして定義されていない限り、シングルトンスコープ豆 にセッションスコープの豆を注入することができない

ばねとして言い換えなければなりません。

つまり、Springは非プロキシのセッションスコープBeanをシングルトンスコープのBeanに挿入することができません。これは、シングルトンスコープのBeanにプロキシされたセッションスコープBeanをインジェクトするのに成功します。

同じユーザーが同じ アプリケーションで同時に有効なセッションを持つと仮定すると、ScopedProxyはどのようなセッションを意味するのでしょうか?

明確にする最初のものはセッションHttpSessionで表されるサーブレットコンテナの構成要素であるということです。 Spring(およびSpring MVC)は、セッションスコープのBean(およびFlash属性などのその他のもの)で抽象化します。

HttpSessionオブジェクトは通常、適切なクッキーによってユーザーに関連付けられます。 HTTPリクエストはCookieにユーザ識別値を提供し、サーブレットコンテナは関連付けられたHttpSessionを取得または作成します。換言すれば、セッションは要求内の情報から識別可能である。あなたまたはSpringはリクエストにアクセスする必要があります。

Spring MVCは通常、ハンドラメソッドに公開されていなくても、明らかにDispatcherServletを通してリクエストにアクセスできます(Spring MVCがあなたからサーブレットAPIを隠そうとすることに注意してください)。

以下は、多少の実装の詳細です。 Spring MVCは、要求オブジェクト(HttpServletRequest)を呼び出しスタックの上まで伝播する代わりに、RequestContextHolderに格納します。

Holderクラスはスレッドバウンド RequestAttributesオブジェクトの形式でWeb要求を露出させます。

これは、サーブレットコンテナ(つまり、非同期非同期)が1つのスレッドで要求を処理するため、これを行うことができます。そのリクエストハンドラスレッドでコードを実行している場合は、そのリクエストにアクセスできます。リクエストにアクセスできる場合はyou have access to the HttpSessionです。

実際の実装はかなり長いです。あなたがそれに入るには、SessionScopeで始まり、あなたの方法を働かせます。

これは具体的なBean型のオブジェクトをSpringに注入しないと、プロキシを注入します。次の例では、JDK proxies(インターフェイスのみ)を使用して、セッションスコープのプロキシの動作を示します。

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

と同様に(しかし、はるかに洗練された)プロキシSessionScopedBeanを作成し、あなたのシングルトン豆(おそらくコントローラ)にこのproxyオブジェクトを注入します。シングルトンBeanがセッションスコープのBeanを使用しようとしているとき、実際にはプロキシを経由しており、プロキシは実際のオブジェクトを取得してその呼び出しを委譲しています。

セッションが0人の場合はどうなりますか? NullPointerExceptionは発生しますか?

春にプロキシが挿入されます。プロキシはnullではありません。それはオブジェクトです。実際のターゲットを取得して使用する方法を知っています。セッションスコープでは、ターゲットが存在しない場合は、作成されてセッションに保存されてから使用されます。


ここで危険なのは、セッションのコンテキスト外でセッションスコープのプロキシを使用することです。前述のように、Servletコンテナは単一のスレッド内で単一の要求を処理することで動作します。要求がバインドされていないスレッドでセッションスコープのBeanにアクセスしようとすると、例外が発生します。

このように、セッションスコープのBeanをスレッドの境界を超えて渡そうとしないでください。 Servlet仕様では、Async ProcessingSpring MVC supports itDefferedResultCallableと使用できます。それについてのブログシリーズ、hereがあります。セッションスコープのBeanを渡すことはできません。ただし、AsyncContextへの参照がある場合は、HttpServletRequestを取得してHttpSessionにアクセスすることができます。

スレッドをディスパッチする方法(またはRunnable)を制御する場合は、要求コンテキストlike the one described hereをコピーするためのテクニックがあります。ここで


は(セッション)スコープやプロキシに関するいくつかの関連記事です:

+0

@PeterRader編集されましたが、より多くの質問を追加してください。新しい質問がある場合は、新しい質問をしてください。 –

2

は、私は非常に簡単な説明を行います

@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 
関連する問題