web-dev-qa-db-ja.com

Java / Spring MVC:子スレッドに要求コンテキストを提供します

Spring WebMVCアプリケーションのいくつかのプロセスを別のスレッドにアウトソーシングしたいという問題があります。グローバルリクエストを使用するクラスuserRightServiceを使用するまで、これは十分に簡単で機能します。それはスレッドでは利用できず、問題が発生します。それはかなり理解できます。

これは私のエラーです:

Java.lang.RuntimeException:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'scopedTarget.userRightsService': Scope 'request' is not active
for the current thread; consider defining a scoped proxy for this bean if
you intend to refer to it from a singleton; nested exception is 
Java.lang.IllegalStateException: Cannot ask for request attribute - 
request is not active anymore!

わかりました。私はこのソリューションを実装することでリクエストコンテキストを維持しようとしています:

非同期タスクエグゼキューターでリクエストスコープを有効にする方法

これは私の実行可能なクラスです:

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class myThread implements Runnable {

  private RequestAttributes context;

  public DataExportThread(RequestAttributes context) {
    this.context = context;
  }

  public void run() {
    RequestContextHolder.setRequestAttributes(context);

そして、これが生成される場所:

final DataExportThread dataExportThread = 
   new myThread(RequestContextHolder.currentRequestAttributes());

final Thread thread = new Thread(myThread);
thread.setUncaughtExceptionHandler((t, e) -> {...});
thread.start();

私が理解している限り、スレッドにcurrentRequestAttributesを格納し、実行時にそれらを復元しますcurrentRequestAttributes ...私にはしっかりと聞こえましたが、エラーはまだ残っています。私は自分の場合にソリューションを適応させるのにいくつかの間違いを犯したと思います。多分誰かが私がエラーを見つけるのを助けることができます。

さまざまなソリューションで多くのスタックオーバーフロースレッドを実行する前に(以下を参照)、次に別の方法を試すことができましたが、これが最も明確で最も簡単な方法であると思われたので、誰かが実装の間違いを見つけてくれるといいですねまたはそれが間違ったアプローチである理由を説明します。

私はすでにこれを試しましたが成功しませんでした:

それが重要な場合:

<org.springframework-version>4.3.4.RELEASE</org.springframework-version>

ところで、アプリケーションをある方法で再構築した方がいいことはわかっています。スレッドではリクエストは必要ありませんが、その場合は非常に複雑になるので、これを回避できればと思います。

-

Edit1:

スレッドで作成できないBeanは次のように始まります:

@Service("userRightsService")
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserRightsService {

-

Edit2:

私もこれを試しました:

しかし、コンテキストは常に空です...

8
Paflow

検索している人のために。 Master_Exのヒントの助けを借りて、私は解決策を見つけました:

ランナブルで:

private HttpServletRequest request;

public void run() {

    final RequestContextListener rcl = new RequestContextListener();
    final ServletContext sc = request.getServletContext();
    rcl.requestInitialized(new ServletRequestEvent(sc, request));

そして、UserRightServiceで、以下を実行する関数を呼び出します。

    SecurityContext context = SecurityContextHolder.getContext();
    Authentication auth = context.getAuthentication();

    context.setAuthentication(getDataExportAuthentication(exportingUser));

@Master_Exのありがとう、あなたの投稿はとても役に立ちました。賞金を差し上げるのが遅すぎてごめんなさい。そうでなければ、私はそれを正しいものとしてマークしたでしょう。

0
Paflow

UserRightsServiceをどのように作成/挿入するのかわからないため、問題を再現できませんでしたが、いくつか試してみてください。

問題は、リクエストが終了するとRequestAttributesが無効になることだと思います(そのため、例外はCannot ask for request attribute - request is not active anymore)、タスクの実行中に発生します。

代わりに、スレッドが生成された場所にUserRightsServiceを挿入して、このインスタンスを引数としてスレッドに渡すことができます。こうすることで、UserRightsServiceを作成できます。リクエストはまだ利用可能であるためです。

それでも、リクエストが終了した後でRequestAttributesにアクセスしようとすると、おそらく失敗します。その場合、リクエストが終了する前、つまりスレッドを実行する前に、必要なすべての値のコピーを作成することをお勧めします。

それでもうまくいかない場合は、タスク内でUserRightsServiceを初期化する方法に関する詳細情報を提供してください。

幸運を!

PS:タスクオブジェクトは手動で作成され、Springによって管理されないため、スレッドクラスのスコープアノテーションは役に立たないと思います。

2
Master_ex