Spring 2.5 の Enum と依存性注入のマップ
質問
次の Java コードがあると仮定します。
public class Maintainer {
private Map<Enum, List<Listener>> map;
public Maintainer() {
this.map = new java.util.ConcurrentHashMap<Enum, List<Listener>>();
}
public void addListener( Listener listener, Enum eventType ) {
List<Listener> listeners;
if( ( listeners = map.get( eventType ) ) == null ) {
listeners = new java.util.concurrent.CopyOnWriteArrayList<Listener>();
map.put( eventType, listeners );
}
listeners.add( listener );
}
}
このコード スニペットは、各リスナーが関心のあるイベントのタイプを伝え、提供されたメソッドがこれらの関係の同時マップを維持する、少し改良されたリスナー パターンにすぎません。
当初、私はこのメソッドを独自のアノテーション フレームワーク経由で呼び出すことを望んでいましたが、さまざまなアノテーション制限というレンガの壁にぶつかりました。持つことはできません java.lang.Enum アノテーションパラメータとして、またさまざまなクラスローダーの問題のセットもあります)そのため、Springを使用することにしました。
これを Spring_ify_ する方法を誰か教えてもらえますか?私が達成したいことは次のとおりです。
1.定義する メンテナー Spring Bean としてのクラス。
2.あらゆるリスナーが登録できるようにする メンテナー XML経由で使用 addListener 方法。Spring doc も Google も例を非常に寛大に提供しています。
これを簡単に達成する方法はありますか?
解決
次のようなことをすると何が問題になるでしょうか。
addListener(Listener, Enum) メソッドを使用して「Maintainer」インターフェイスを定義します。
Maintainer を実装する DefaultMaintainer クラス (上記と同様) を作成します。
次に、各 Listener クラスで Maintainer インターフェイスを「注入」します (コンストラクター注入が良い選択かもしれません)。その後、リスナーは自身を Maintainer に登録できます。
それ以外では、現時点で Spring に関してどのような困難があるのか正確に 100% 明確ではありません。:)
他のヒント
少し話が逸れますが (これは Spring に関するものではないので)、AddListener の実装に競合状態があります。
if( ( listeners = map.get( eventType ) ) == null ) {
listeners = new java.util.concurrent.CopyOnWriteArrayList<Listener>();
map.put( eventType, listeners );
}
listeners.add( listener );
2 つのスレッドが同時にこのメソッドを呼び出した場合 (以前はリスナーを持たなかったイベント タイプの場合)、map.get(eventType ) は両方のスレッドで null を返し、各スレッドは独自の CopyOnWriteArrayList (それぞれに 1 つのリスナーを含む) を作成します。一方のスレッドが他方のスレッドによって作成されたリストを置き換え、最初のリスナーは忘れられます。
これを修正するには、次のように変更します。
private Map<Enum, List<Listener>> map;
...
map.put( eventType, listeners );
に:
private ConcurrentMap<Enum, List<Listener>> map;
...
map.putIfAbsent( eventType, listeners );
listeners = map.get( eventType );
1) Maintainer クラスを Spring Bean として定義します。
標準の Spring 構文が適用されます。
<bean id="maintainer" class="com.example.Maintainer"/>
2) addListenerメソッドを使用して、XML経由で各種リスナーをMaintainerに登録できるようにします。Spring doc も Google も例を非常に寛大に提供しています。
これはさらに厄介です。あなた できた 使用 MethodInvokingFactoryBean
個別に電話する maintainer#addListener
, 、 そのようです:
<bean id="listener" class="com.example.Listener"/>
<bean id="maintainer.addListener" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="maintainer"/>
<property name="targetMethod" value="addListener"/>
<property name="arguments">
<list>
<ref>listener</ref>
<value>com.example.MyEnum</value>
</list>
</property>
</bean>
ただし、これは扱いにくく、間違いが発生する可能性があります。私はプロジェクトで同様のことを試み、代わりに役立つ Spring ユーティリティ クラスを作成しました。現時点ではソースコードが入手できないので、私が行った実装方法を説明します。
1) リッスンするイベント タイプをリファクタリングします。 MyListener
インターフェース
public interface MyListener extends Listener {
public Enum[] getEventTypes()
}
登録方法が次のように変わります。
public void addListener(MyListener listener)
2) コンテキスト内の関連するリスナーをすべて検索し、見つかったリスナーごとに maintainer#addListener を呼び出す Spring ヘルパー クラスを作成します。私なら始めます BeanFilteringSupport
, 、また実装します BeanPostProcessor
(または ApplicationListener
) すべての Bean がインスタンス化された後に Bean を登録します。
あなたが言った "...java.lang.enumを「アノテーションパラメーション...」として持つことはできません。
それは間違っていると思います。私は最近、プロジェクトで次のようなものを使用しました。
public @interface MyAnnotation {
MyEnum value();
}
皆様、ご回答ありがとうございました。まず、すべての回答に対する簡単なフォローアップです。
1.(alexvictor) はい、コンクリートを使用できます 列挙型 アノテーションパラメータとして使用しますが、そうではありません java.lang.Enum.
2.flicken が提供した答えは正しいですが、残念ながら少し怖いです。私は Spring の専門家ではありませんが、このように物事を行う (Spring にアクセスしやすくするためのメソッドを作成する) これは少しやりすぎのようです。 メソッド呼び出しFactoryBean 解決。時間と労力を割いていただき、心から感謝を申し上げたかったのですが。
3.Phill の答えは少し変わっています (リスナー Bean を注入する代わりに、そのメンテナーを注入してください!) が、利用可能な答えの中で最もクリーンだと思います。この道を進んでいこうと思います。
改めて、ご協力いただきまして誠にありがとうございます。