4

メソッドをさまざまな実装に委任できる動的プロキシを作成したいと思います(メソッドの呼び出しごとに、潜在的に異なるオブジェクトが選択されます)。そして、あるプロキシメソッドが別のプロキシメソッドを呼び出すときのように、ポリモーフィック効果を実現したいのですが、オブジェクト選択メカニズムが再び適用されます。

さて、十分な混乱、ここに例があります:

interface IService {
  void a();
  void b();
}

class HappyService implements IService {
  public void a() {
    System.out.println("Happy a");
    b();
  }

  public void b() {
    System.out.println("Happy b");
  }
}

class SadService implements IService {
  public void a() {
    System.out.println("Sad a");
    b();
  }

  public void b() {
    System.out.println("Sad b");
  }
}

ここで、メソッドの呼び出しとメソッドの呼び出しIServiceを常に選択するプロキシを作成したいと思います。最初に頭に浮かぶのは次のとおりです。HappyServicea()SadServiceb()

InvocationHandler h = new InvocationHandler() {
  @Override
  public Object invoke( final Object proxy, final Method method, final Object[] args ) throws Throwable {
    Object impl;
    if (method.getName().equals("a")) {
      impl = new HappyService();
    } else if (method.getName().equals("b")) {
      impl = new SadService();
    } else {
      throw new IllegalArgumentException("Unsupported method: " + method.getName());
    }
    return method.invoke(impl, args);
  }
};
IService service = (IService)Proxy.newProxyInstance( IService.class.getClassLoader(), new Class[]{ IService.class }, h );
service.a();

これは印刷します:

Happy a
Happy b

ええ、それは、b()insideの呼び出しがa()動的プロキシについて何も知らないためです。

それで、どうすれば私の目標を最もよく達成できますか?私の希望する出力は次のとおりです。

Happy a
Sad b

おそらくnew HappyService()、呼び出しハンドラーの内部をさらに別のプロキシに置き換えることができます。このプロキシは、メソッドのみをに渡し、他のすべてのメソッドa()HappyService元のプロキシにリダイレクトします。しかし、おそらくより良い/より簡単な解決策がありますか?

4

1 に答える 1

3

プロキシは、呼び出し元が「実際の」実装ではなくプロキシへの参照を持っている場合にのみ、オブジェクト間呼び出しをインターセプトするために使用できます。ここでは、両方の実装が直接a()呼び出しb()ているので、もちろん、で呼び出しますthis。あなたがしたいことはプロキシによって達成することはできません。

ただし、AOPを使用して、たとえばAspectJを使用し、コンパイル時ウィービング(aspectj-maven-pluginでも使用可能)またはロード時ウィービングを使用してこれを行うことができます。大まかに言って、実装内のメソッドとメソッドの呼び出しサイトにポイントカットを使用してアスペクトを作成し、元の呼び出しを別の呼び出しに置き換える可能性のある実行をアドバイスします。テストされていないコードですが、次のようになります。a()b()IService

public aspect IServiceAspect {
    private IService happy = new HappyService(); // Assuming these are stateless services
    private IService sad = new SadService();     // that can be shared.

    // The pointcut is inside an IService implementation, and is a call to the a() method
    pointcut aCall(): within(IService+) && call(* IService.a());

    pointcut bCall(): within(IService+) && call(* IService.b());

    around(): aCall() && !within(HappyService) { // To avoid infinite recursion
        return happy.a();
    }

    around(): bCall() && !within(SadService) {
        return sad.b();
    }
}

HappyService次に、またはSadService実装を直接提供できますIService。どちらも変更されています。のすべてのメソッドに一致する単一のポイントカットを作成し、例のようにIService、メソッド名に基づいて(thisJoinPointアドバイスで使用して)動的にルーティングを行うこともできます。

アスペクトは、アノテーションを使用して宣言することもできます。

于 2012-10-12T12:01:37.763 に答える