web-dev-qa-db-ja.com

コードビハインドでカスタムアノテーションを作成する方法

独自のカスタムアノテーションを作成したいと思います。私のフレームワークはスタンドアロンですJavaアプリケーション。誰かが彼のpojoクラスに注釈を付けると、背後にある「隠された」コードがメソッドをトリガーします。

たとえば、今日のJava EEには_@MessageDriven_アノテーションがあります。そして、クラスに_@MessageDriven_アノテーションを付け、さらにMessageListenerインターフェイスを実装すると、背後にコードがあります。トリガーonMessage(Message msg)。メッセージがキュー/トピックから到着したとき。

Pojoに追加してMyCustomMessageListenerを実装できるアノテーション(_@MyMessageDriven_)を作成するにはどうすればよいですか。

私が望む結果は、実装されたインターフェイスのメソッドをトリガーする「私の」の「隠された」コードのトリガーです(以下のサンプルiで正確に動作します)。

36
rayman

このブログエントリ(archive.orgのスナップショット) を読むことをお勧めします。著者は、Springのコンポーネントスキャン機能にアクセスできることを著者が記憶している時点までです。

最初の問題は、クラスパスをスキャンして、カスタムアノテーションを持つクラスを見つけることです。これが完了すると、スタンドアロンアプリケーションにオブジェクトがあり、それを通して object.getClass().getAnnotations() を使用して、カスタムを保持するオブジェクトに追加する必要があるリスナーまたはカスタム動作を注入できます。注釈。

次のカスタムアノテーションがあるとします。

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMessageDriven {}

そして、あなたはあなたのアプリケーションでいくつかのクラスを使用します:

@MyMessageDriven
public class MyObject {}

これで、アプリケーションの適切な場所に、MyMessageDrivenを保持するすべてのクラスを提供するメソッドが必要になります。

Set<Class<?>> findAllMessageDrivenClasses() {
  final StopWatch sw = new StopWatch();
  sw.start();
  final Reflections reflections = new Reflections("org.projectx", new TypeAnnotationsScanner());
  Set<Class<?>> allMessageDrivens = reflections.getTypesAnnotatedWith(MyMessageDriven.class); // NOTE HERE
  sw.stop();
  return allMessageDrivens;
}

これにより、アプリケーションには(1)アプリケーション内のオブジェクトにアクセスできるか、(2)アプリケーション内のすべてのオブジェクトにビジターまたはイテレーターパターンがあるかのいずれかのポイントがあると思います。そのため、ある時点で、すべてのターゲットオブジェクトがobjectsであると仮定します。

Set<Class<?>> msgDrivenClasses = findAllMessageDrivenClasses();
for (Object o : objects) {
  if (msgDrivenClasses.contains(o.getClass()) {
    invokeTheMessageListener(o);
  }
}

一方、MyMessageListenerを持つオブジェクトが見つかった場合に利用できるMyMessageDrivenの実装があるはずです。

void invokeTheMessageListener(Object o) {
  theMessageListener.onMessage(o);
}

この回答はブログエントリから調整されているため、ライブラリの構成についてはブログを参照してください。そして最後になりましたが、これは問題のサンプルコードであり、パターン互換性が高くエレガントなスタイルにリファクタリングできます。

Updatetargetedオブジェクトは自身のリスナーを認識する必要があります。そこで、次のアプローチをお勧めします。インターフェイスMyMessageListenerAwareを用意しましょう。

interface MyMessageListenerAware {
  MyMessageListener getMyMessageListener();
}

// and this is the original MyMessageListener
interface MyMessageListener {
  void onMessage(Object o);
}

これで、ターゲットオブジェクトは上記のインターフェイスを実装する必要があります。

class MySampleObject implements MyMessageListenerAware {

  public MyMesssageListener getMyMessageLisener() {
    return mySampleObjectImplementationOfMyMessageListener;
  }

}

これにより、メソッドinvokeTheMessageListenerは次のようになります。

void invokeMessageListener(Object o) {
  if (o instance MyMessageListenerAware) {
    MyMessageListener l = ((MyMessageListenerAware) o).getMyMessageListener();
    l.onMessage(o);
  }
}

ですが、 Visitor または Strategy パターンについて読むことを強くお勧めします。あなたが目指すことは、特定のオブジェクトがアプリケーションの一般的なオブジェクト/イベントに反応/行動/処理する必要があるように思えますが、それぞれの解釈/アルゴリズム/実装でeach.

35
nobeh

次のような注釈を作成します。

_ public @interface MyMessageDriven{
 }
_

そして、次のような注釈を適用できるインターフェースがあります:

_public interface MyMessagListener {

    public void message();
}



@MyMessageDriven  
public class MyMessage implements MyMessagListener  {
   public void message(){
     System.out.println(" I am executed")
   }
} 
_

クラスローダーを使用して上記のクラスをロードし、リフレクションを使用して注釈が存在することを確認します。

存在する場合は、ロードされたインスタンスを使用して実行します。

_  Object obj = ClassLoader.getSystemClassLoader().loadClass("MyMessage").newInstance();
  MyMessagListener mml =  (MyMessagListener) obj;
  mml.message();
_

MyListenerクラスまたはMessageListenerを実装する他のクラスに配置できるリスナー実装。

この場合、message()の実装を提供する必要があります。

ただし、このクラスをロードする必要があります。ここでより重要なことは、MyMessageクラスのロード方法です。

これは、MyMessageクラスに存在するメタデータに基づいています。同様の方法で、リアルタイムシナリオでも同様に機能します。

注釈は、提供されたデータに基づいて何かを行うというクラスへのメタデータです。このメタデータがMyMessageクラスに存在しない場合、message()メソッドを実行する必要はありません。

これがお役に立てば幸いです。

10
UVM