42

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

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

@MyMessageDrivenpojoに追加して実装できるannotation()を作成するにはどうすればよいですかMyCustomMessageListener

私が望む結果は、(私の)「隠された」コードのトリガーであり、実装されたインターフェースのメソッドをトリガーします(以下で書いたサンプルで動作するのとまったく同じです)。

4

2 に答える 2

38

このブログ エントリ (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);
}

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

更新:対象となるオブジェクトが自身のリスナーを認識している必要があります。そこで、次のアプローチを提案します。インターフェースを用意しましょう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);
  }
}

ただし、訪問者または戦略パターンについて読むことを強くお勧めします。あなたがやろうとしていることは、特定のオブジェクトがアプリケーション内の共通のオブジェクト/イベントに反応/行動/処理する必要があるように思えますが、それぞれに独自の解釈/アルゴリズム/実装があります。

于 2012-04-18T08:21:57.363 に答える
10

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

 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();

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

この場合、message()何をしようとしているかの実装を提供する必要があります。

しかし、このクラスはロードする必要があり、ここでさらに重要なことは、MyMessage クラスがどのようにロードされるかです。

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

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

これがあなたを助けることを願っています。

于 2012-04-18T08:44:04.977 に答える