web-dev-qa-db-ja.com

スコープ「セッション」は現在のスレッドに対してアクティブではありません。 IllegalStateException:スレッドバインド要求が見つかりません

セッションごとに一意にしたいコントローラーがあります。春のドキュメントによると、実装には2つの詳細があります。

1。初期Web設定

要求、セッション、およびグローバルセッションレベル(WebスコープのBean)でBeanのスコープをサポートするには、Beanを定義する前にいくつかのマイナーな初期構成が必要です。

ドキュメントに示されているように、次をweb.xmlに追加しました。

<listener>
  <listener-class>
    org.springframework.web.context.request.RequestContextListener
  </listener-class>
</listener>

2。依存関係としてスコープされたBean

HTTP要求スコープBeanを(たとえば)別のBeanに注入する場合、スコープBeanの代わりにAOPプロキシを注入する必要があります。

以下に示すように、@ScopeproxyModeを提供してBeanに注釈を付けました。

@Controller
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class ReportBuilder implements Serializable {
    ...
    ...
}

問題

上記の構成にもかかわらず、次の例外が発生します。

org.springframework.beans.factory.BeanCreationException:「scopedTarget.reportBuilder」という名前のBeanを作成中にエラーが発生しました。スコープ「セッション」は現在のスレッドに対してアクティブではありません。シングルトンから参照する場合は、このBeanのスコーププロキシを定義することを検討してください。ネストされた例外はJava.lang.IllegalStateExceptionです:スレッドバインドリクエストが見つかりません:実際のWebリクエスト以外のリクエスト属性を参照していますか、それとも元の受信スレッド外でリクエストを処理していますか?実際にWebリクエスト内で操作しているにもかかわらずこのメッセージを受信する場合、コードはおそらくDispatcherServlet/DispatcherPortletの外部で実行されています。この場合、RequestContextListenerまたはRequestContextFilterを使用して現在のリクエストを公開します。

更新1

以下は私のコンポーネントスキャンです。 web.xmlには次のものがあります。

<context-param>
  <param-name>contextClass</param-name>
  <param-value>
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  </param-value>
</context-param>

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>org.example.AppConfig</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

そしてAppConfig.Javaの次は:

@Configuration
@EnableAsync
@EnableCaching
@ComponentScan("org.example")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements AsyncConfigurer {
  ...
  ...
}

更新2

再現可能なテストケースを作成しました。これは非常に小さなプロジェクトなので、違いはありますが、同じエラーが発生します。かなりの数のファイルがあるので、tar.gzとして megafileupload にアップロードしました。

52
Jon

私は自分の質問に答えています。なぜなら、それが原因と可能な解決策のより良い概要を提供するからです。彼が原因を突き止めたので、@ Martinにボーナスを授与しました。

原因

@Martinが示唆するように、原因は複数のスレッドの使用です。 Spring Guide で説明されているように、リクエストオブジェクトはこれらのスレッドでは使用できません。

DispatcherServletRequestContextListener、およびRequestContextFilterはすべてまったく同じことを行います。つまり、HTTP要求オブジェクトを、その要求を処理しているスレッドにバインドします。これにより、リクエストスコープおよびセッションスコープのBeanがコールチェーンのさらに下で利用可能になります。

ソリューション1

要求オブジェクトを他のスレッドで使用可能にすることは可能ですが、システムにいくつかの制限があり、すべてのプロジェクトで機能しない場合があります。このソリューションは マルチスレッドWebアプリケーションでスコープされたリクエストBeanにアクセスする :から取得しました。

私はこの問題をなんとか乗り越えました。 SimpleAsyncTaskExecutor/WorkManagerTaskExecutorの代わりにThreadPoolExecutorFactoryBeanを使用し始めました。利点は、SimpleAsyncTaskExecutorがスレッドを再利用しないことです。それは解決策の半分に過ぎません。解決策の残りの半分は、RequestContextFilterの代わりにRequestContextListenerを使用することです。 RequestContextFilter(およびDispatcherServlet)には、基本的に子スレッドが親コンテキストを継承できるthreadContextInheritableプロパティがあります。

ソリューション2

他の唯一のオプションは、リクエストスレッド内でセッションスコープBeanを使用することです。私の場合、これは次の理由で不可能でした。

  1. コントローラーメソッドには@Asyncの注釈が付けられます。
  2. コントローラーメソッドは、並列ジョブステップにスレッドを使用するバッチジョブを開始します。
29
Jon

問題はSpringアノテーションではなく、デザインパターンにあります。異なるスコープとスレッドを混在させます:

  • シングルトン
  • セッション(またはリクエスト)
  • ジョブのスレッドプール

シングルトンはどこでも使用できますが、大丈夫です。ただし、セッション/リクエストスコープは、リクエストにアタッチされているスレッドの外部では使用できません。

非同期ジョブは、リクエストまたはセッションがもう存在しない場合でも実行できるため、リクエスト/セッション依存のBeanを使用することはできません。また、別のスレッドでジョブを実行している場合、どのスレッドが発信元リクエストであるかを知る方法もありません(この場合、aop:proxyは役に立たないことを意味します)。


コードは、ReportController、ReportBuilder、UselessTask、ReportPageの間にcontractを作成したいように見えると思います。 UselessTaskからのデータを保存し、ReportControllerまたはReportPageでデータを読み取るために単純なクラス(POJO)を使用する方法はありますか?ReportBuilderは使用しないでくださいもうありませんか?

49
Martin Strejc

他の誰かが同じ点にこだわっていた場合、以下は私の問題を解決しました。

Web.xmlで

 <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
  </listener>

セッション中コンポーネント

@Component
@Scope(value = "session",  proxyMode = ScopedProxyMode.TARGET_CLASS)

Pom.xmlで

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
31
Mohit

ドキュメントごと

Spring Web MVC内、つまりSpring DispatcherServletまたはDispatcherPortletによって処理されるリクエスト内でスコープBeanにアクセスする場合、特別な設定は必要ありません:DispatcherServletとDispatcherPortletはすべての関連する状態を既に公開しています。

Spring MVCの外で実行している場合(DispatchServletによって処理されない)、RequestContextListenerだけでなくContextLoaderListenerを使用する必要があります。

Web.xmlに以下を追加します

   <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
    </listener>        

そのスコープでBeanを維持するために、Springにセッションを提供します

更新:他の回答によると、@Controllerは、Spring MVCコンテキストを使用しているときにのみ意味があるため、@ Controllerはコードの実際の目的を果たしていません。それでも、セッションスコープ/リクエストスコープでどこにでもBeanを注入できます(特定のスコープでBeanを注入するだけでSpring MVC/Controllerは必要ありません)。

更新: RequestContextListener は、現在のスレッドのみにリクエストを公開します。
2つの場所でReportBuilderを自動配線しました

1。 ReportPage-ここでは、SpringがReport Builderを適切に挿入していることがわかります。これは、まだ同じWebスレッドにいるためです。 ReportBuilderがこのようにReportPageに挿入されるように、コードの順序を変更しました。

log.info("ReportBuilder name: {}", reportBuilder.getName());
reportController.getReportData();

私は追加されたデバッグ目的のために、ログがあなたのロジックに従って追跡する必要があることを知っていました。


2。 UselessTasklet-ここでは例外が発生しました。これは、Spring Batchによって作成された別のスレッドで、RequestContextListenerによって要求が公開されていないためです。


Spring BatchにReportBuilderインスタンスを作成およびインジェクトするための異なるロジックが必要です(Spring Batchパラメーターを使用し、Future<ReportBuilder>を使用して、今後の参照用に返すことができます)

7
Mani

https://stackoverflow.com/a/30640097/2569475

この問題については、上記のURLで回答を確認してください

実際のWeb要求の外部で要求スコープBeanを使用する

2
Deepak

ファイルに次のコードを追加して、この問題を修正しました。

@Component
@Scope(value = "session",  proxyMode = ScopedProxyMode.TARGET_CLASS)

XML構成-

<listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener 
        </listener-class>
</listener>

上記のJava設定を使用して行うことができます-

@Configuration
@WebListener
public class MyRequestContextListener extends RequestContextListener {
}

no-xml設定でRequestContextListenerを追加する方法?

私は春バージョン5.1.4.RELEASEとno needを使用してpomに以下の変更を追加しています。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.10</version>
</dependency>
2
Noman Akhtar

私の答えは、OPが説明する一般的な問題の特別なケースを指しますが、誰かが助けになる場合に備えて追加します。

@EnableOAuth2Ssoを使用すると、SpringはOAuth2RestTemplateをアプリケーションコンテキストに配置します。このコンポーネントは、スレッドにバインドされたサーブレット関連のものを想定しています。

私のコードには、自動配線RestTemplateを使用する非同期メソッドがスケジュールされています。これはDispatcherServletの内部では実行されていませんが、SpringはOAuth2RestTemplateを挿入していたため、OPが記述するエラーが発生しました。

解決策は、名前ベースの注入を行うことでした。 Java構成:

@Bean
public RestTemplate pingRestTemplate() {
    return new RestTemplate();
}

そしてそれを使用するクラスで:

@Autowired
@Qualifier("pingRestTemplate")
private RestTemplate restTemplate;

これで、Springは意図したサーブレットフリーRestTemplateを注入します。

1
Willie Wheeler

プロトタイプを除き、デフォルトのシングルトンスコープとは異なるスコープが必要なBeanで定義する必要があります。例えば:

<bean id="shoppingCart" 
   class="com.xxxxx.xxxx.ShoppingCartBean" scope="session">
   <aop:scoped-proxy/> 
</bean>
0
Kunwar Babu