(インターフェースではなく)抽象クラスのプロキシーを作成するためのjava.lang.reflect.Proxyの代替


89

ドキュメントによると:

[ java.lang.reflect.]Proxyは、動的プロキシクラスとインスタンスを作成するための静的メソッドを提供します。また、これらのメソッドによって作成されるすべての動的プロキシクラスのスーパークラスでもあります。

このnewProxyMethodメソッド(動的プロキシの生成を担当)には、次の署名があります。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

残念ながら、これにより、(特定のインターフェイスを実装するのではなく)特定の抽象クラスを拡張する動的プロキシを生成できなくなります。これは、「すべての動的プロキシのスーパークラス」であると考えると理にかなっています。これにより、別のクラスがスーパークラスになるのを防ぐことができます。java.lang.reflect.Proxy

したがって、特定の抽象クラスから継承java.lang.reflect.Proxyする動的プロキシを生成し、抽象メソッドへのすべての呼び出しを呼び出しハンドラーにリダイレクトできる代替手段はありますか?

たとえば、抽象クラスがあるとしますDog

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

次のことができるクラスはありますか?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler

回答:


123

これは、使用して行うことができるJavassistの(参照ProxyFactory)またはCGLIBを

Javassistを使用したAdamの例:

私(Adam Paynter)は、Javassistを使用してこのコードを作成しました。

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

これはこの出力を生成します:

横糸!
メソッドハンドラを介したpublicabstract void mock.Dog.fetch()の処理

10
+1:まさに私が必要なものです!サンプルコードを使用して回答を編集します。
Adam Paynter 2010

proxyFactory.setHandler()は非推奨です。をご利用くださいproxy.setHandler
AlikElzin-kilaka 2016年

@axtavtは、上記のコードの実装またはインターフェイスのオブジェクト「Dog」ですか?
stackoverflow 2018

-7

このような場合にできることは、抽象クラスの既存のメソッドに呼び出しをリダイレクトするプロキシハンドラーを用意することです。

もちろんコーディングする必要がありますが、非常に簡単です。プロキシを作成するには、彼にを与える必要がありますInvocationHandler。その後invoke(..)、呼び出しハンドラーのメソッドでメソッドタイプを確認するだけで済みます。ただし、注意してください。メソッドの型を、抽象クラスの宣言された型ではなく、ハンドラーに関連付けられた基になるオブジェクトと照合する必要があります。

犬のクラスを例にとると、呼び出しハンドラーのinvokeメソッドは次のようになります(既存の関連付けられた犬のサブクラスは.. well ...と呼ばれますdog

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

しかし、私が不思議に思うことがあります:私はdogオブジェクトについて話しました。ただし、Dogクラスは抽象であるため、インスタンスを作成できないため、既存のサブクラスがあります。さらに、プロキシソースコードを厳密に調べると、(Proxy.java:362で)インターフェイスを表さないClassオブジェクトのプロキシを作成できないことがわかります。

ですから、現実とは別に、あなたがやりたいことは完全に可能です。


1
私があなたの答えを理解しようとしている間、私に耐えてください...私の特定のケースでは、プロキシクラス(実行時に生成される)をのサブクラスにしたいですDog(たとえば、Poodle実装するクラスを明示的に記述していませんfetch())。したがって、dogメソッドを呼び出す変数はありません...混乱している場合は申し訳ありませんが、これについてもう少し考えなければなりません。
Adam Paynter 2010

1
@ Adam-バイトコードを操作しないと、実行時にサブクラスを動的に作成することはできません(CGLibはこのようなことをすると思います)。簡単に言うと、動的プロキシはインターフェイスをサポートしますが、抽象クラスはサポートしません。これは、2つが非常に異なる概念であるためです。抽象クラスを適切な方法で動的にプロキシする方法を考えることはほとんど不可能です。
Andrzej Doyle

1
@Andrzej:私が求めているものにはバイトコード操作が必要であることを理解しています(実際、ASMを使用して問題の解決策をすでに作成しています)。また、Javaの動的プロキシはインターフェイスのみをサポートしていることも理解しています。おそらく私の質問は完全には明確ではありませんでした-私が必要なことを行う他のクラス(つまり、以外のものjava.lang.reflect.Proxy)が利用可能かどうかを尋ねています。
Adam Paynter 2010

2
まあ、長いことを短くするために...いいえ(少なくとも標準のJavaクラスでは)。バイトコード操作を使用すると、空が限界です!
Riduidel 2010

9
それは本当に質問への答えではないので、私は反対票を投じました。OPは、インターフェースではなくクラスをプロキシしたいと述べており、java.lang.reflect.Proxyではそれが不可能であることを認識しています。あなたは単にその事実を繰り返し、他の解決策を提供しません。
jcsahnwaldt Reinstate Monica 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.