0

ゲッター/セッター メソッドを備えたインターフェイスで構成されたオブジェクト モデルがあります。これらのオブジェクトの実装は動的プロキシを使用して作成され、フィールドの値が (JavaBean 命名規則を使用して) 暗黙的に Map に格納されます。

これらのインターフェースにメソッドを追加して、ビジネス ロジックを提供したいと考えています (単なる POJO のコレクションではなく、実際のオブジェクト モデルのように)。

私が最初に考えたのは、各インターフェイスを実装するが、ビジネス メソッドの実装のみを提供する抽象クラスを作成することでした。次に、これらの実装を InvocationHandler の Map と組み合わせて使用​​して、インターフェイスの完全な実装を提供します。

何かのようなもの:

interface ModelObject extends BaseModel {
    void setFoo(String foo);
    String getFoo();

    void doSomething();
}

public abstract class ModelObjectImpl implements ModelObject {

    @Override
    public void doSomething()
    {
        // Do something
    }
}

public class ModelObjectInvocationHander implements InvocationHandler {

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName)) {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        return method.invoke(modelObject, args);
    }
}

抽象クラスのインスタンスを作成できないことを除けば、このような (しかし明らかにより複雑な) 方法が機能します。BusinessObjectImpl を非抽象化し、呼び出されることのない getter/setter メソッドの何もしない実装を追加することもできますが、それはコードを醜くし、メンテナンスの問題を引き起こすだけです。また、BusinessObjectImpl を実際には BusinessObject インターフェイスを実装しないようにすることもできますが、実装とインターフェイスの間の適切なバインディングが壊れ、インターフェイスと「実装」が同期しなくなるとエラーが発生します。

これらのビジネス メソッドを呼び出すために使用できる Java リフレクションの巧妙なトリックはありますか?

更新: 既に実装されている Java 動的プロキシ フレームワークとJavassistを組み合わせて、抽象実装クラスのプロキシを作成しました。これにより、必要に応じてビジネス メソッドが追加されるまで、既存のモデル インターフェイスをまったく変更する必要がなくなります。オブジェクトに動作を追加する機能が用意されました。真のオブジェクト指向コードを今すぐ書き始めるかどうかは、開発者次第です。

public class ModelObjectInvocationHandler implements InvocationHandler
{

    public ModelObjectInvocationHandler(Class<ModelImplementation<? extends BaseModel>> implementationClass)
    {
        if (implementationClass != null) 
        {
            ProxyFactory factory = new ProxyFactory();
            factory.setSuperclass(implementationClass);
            try
            {
                modelObject = (ModelObject) factory.create(new Class<?>[0], new Object[0]);
            }
            catch (Exception e)
            {
                // Exception handling
            }
        }
    }

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName))
        {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        if (modelObject != null)
        { 
            return method.invoke(modelObject, args);
        }

        return null;
    }
}

実行時に、実装クラスをスキャンしてMap<Class<? extends BaseModel>, Class<ModelImplementation>>. インターフェイスの動的プロキシを作成するとき、マップでその実装クラスを見つけて、InvocationHandler. Bean 名として一致しないメソッドは、実装クラスのプロキシに委譲されます。もちろん、モデル インターフェイス内のクラス階層と多重継承を考慮する必要があるため、それよりも少し複雑ですが、理論は正しいです。

4

2 に答える 2

0

それを行う標準的なJavaリフレクションのトリックは知りません。クラスのロード時にcglibまたはを使用して、抽象クラスを動的に拡張できます。これにより、オブジェクトが不要javaassistになるため、パフォーマンスが少し向上します。Proxy代わりに、新しいクラスを作成するときに getter/setter メソッドを直接実装できます。

これらのトリックを使用しない 3 つ目の方法は、委任パターンを使用することです。

public class ModelObjectImpl implements ModelObject {
  private final ModelObject delegate;

  public ModelObjectImpl(ModelObject delegate) { 
    this.delegate = delegate;
  }

  @Override
  public void doSomething() { /* Do something */ }

  @Override
  public String getFoo() { return delegate.getFoo(); }

  @Override
  public void setFoo(String foo) { delegate.setFoo(foo); }
}

インターフェイスのゲッター/セッター メソッドを実装するプロキシをコンストラクターdelegate引数にフィードします。ただし、これはスタブ メソッドよりも優れているように見えますが(少なくとも私にとっては)、依然として重複したコードです。したがって、そのような動的クラスが本当に必要な場合は、動的バイトコード生成を使用してください。

参考文献:

于 2013-07-13T22:56:05.530 に答える