2016-10-06 7 views
1

私はSpring MVCを使用してWebアプリケーションを開発していますが、各リクエストがいつ発生するかを示すリクエストスコープDateのリクエストスコープが必要です。このようなDate beanを定義するために、私はアプリケーションコンテキストxmlに次のBean定義を記述しました。コンストラクタインジェクションを使用してスコープ付きプロキシBeanをシングルトンBeanに挿入する方法

<bean id="now" 
     class="java.util.Date" 
     scope="request"> 
    <aop:scoped-proxy/> 
</bean> 

フィールドインジェクションを使用してこのビーンをシングルトンBeanに注入すると、正常に動作します。

public class ASingletonBean { 
    @Autowired 
    private Date now; 
    ... 
} 

しかし、私はフィールドの注入を推奨していないので使用したくありません。私のIDEでは、代わりにコンストラクタインジェクションを使用することを提案しています。

public class ASingletonBean{ 
    private final Date now; 

    @Autowired 
    public ASingletonBean(Date now) { 
     this.now = now; 
    } 
} 

上記のコードは、アプリケーションの起動時に以下の例外をスローします。

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'scopedTarget.java.util.Date#0': 
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: 
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? 
If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: 
In this case, use RequestContextListener or RequestContextFilter to expose the current request. 

どうすればこのエラーを回避できますか?

答えて

1

しないでください。リクエストごとに新しいBeanを作成する必要はありません。簡単に避けることができるオーバーヘッドです。代わりに、例えばjava.util.function.Supplierを実装するクラス+豆作成:

@Component 
class DateTimeProvider implements java.util.function.Supplier<Date> { 
... 
} 

をしたあと、「あなたの `ASingletonBeanにこのjava.util.function.Supplier<Date>を注入。これにより、リクエストを処理する際に現在の日付/時刻を取得することができます。

そして、いくつかの追加の注意事項:代わりにjava.util.Dateの

  • 使用JodaTimeまたはJDK8 JavaTimeのAPI、
  • あなたはJDK8(java.util.function.SupplierがJDK8に追加されました)を使用することができないならば、あなたはあなたを作成することができます
  • 受信したリクエストに非常に正確なタイミングが必要な場合は、ある種の「タイムスタンピング」フィルタを作成することを検討してください。最も一般的には、org.springframework.web.filter.OncePerRequestFilterクラスを拡張します。 -

編集コメントからの質問に答えるために:

  • 「なぜそれがリクエストスコープのBeanを使用することが好ましい」 - Beanは、常にあなたが作成する任意のビジネス・コンポーネントに注入されているだろう。ちょうど「要求タイムスタンプ」を持っているためにちょっと思えます。

  • 外部から受け取ったリクエストオブジェクトは、「ドメイン要求」(タイムスタンプを含む)の何らかの種類に変換され、そのドメイン形式でのみ内部的に処理されます。 (詳細については、六角形アーキテクチャーa.k.aポートとアダプター、ドメイン・ドライバーの設計)を参照してください。なぜそうなのか? HTTPリクエストの手段によってのみシステムに入る要求が、JMSメッセージまたはバッチインポート操作の形でシステムに入る可能性があることを容易に想像できるので、新しいアダプタをシステムに提供する必要があります。コア・ドメイン内のロジックは変更されません。

  • Springブートを使用している場合は、org.springframework.web.filter.OncePerRequestFilter#OncePerRequestFilterのBeanを作成し、実装する必要がある唯一の方法でロジックを実装すれば十分です。 Springは自動的にフィルタを使用します。

+0

ありがとうございます。私は実際にアプリケーションの多くの場所でリクエストが発生したときの 'Date'オブジェクトを使用したいと思います。だから、「サプライヤ」を使ったアプローチは私の場合にはあまり適していません。私は 'OncePerRequestFilter'を使うことに興味があります。どうすればそれを使うことができますか、なぜリクエストスコープのBeanを使うのが望ましいのでしょうか? – umainyosu

0

Rafal Gの答えは意味があり、良い習慣を説明していますが、私は問題を解決するための汚い方法も見つけました。

回避策は、Dateではなく、「プロバイダ」Beanを注入することです。まず、プロバイダーBeanをaop:scoped-proxyというBeanとして定義します。

<bean id="currentLocalDateTimeProvider" 
     class="com.example.CurrentLocalDateTimeProvider" 
     init-method="init" 
     scope="request"> 
    <aop:scoped-proxy/> 
</bean> 

CurrentLocalDateTimeProviderさんは以下のように定義されます。

import java.time.LocalDateTime; 

public class CurrentLocalDateTimeProvider { 
    private LocalDateTime now; 

    public void init() { 
     now = LocalDateTime.now(); 
    } 

    public LocalDateTime now() { 
     return now; 
    } 
} 

そしてシングルトン豆にこれを注入します。

次に、nowメソッドを呼び出して、リクエストタイムスタンプを取得します。

これは誰かを助けることを願っています:)

関連する問題