8

Java は、特定のインターフェイスのプロキシ クラスを生成し、プロキシ クラスのインスタンスを提供します。しかし、プロキシ オブジェクトを特定のオブジェクトに型キャストすると、Java はこれを内部でどのように処理するのでしょうか? これは特別なシナリオとして扱われますか?

たとえば、提供されたインターフェースのメソッドを使用してインターフェース Java によって作成されたプロキシ クラスを渡すことによってプロキシ オブジェクトを作成し、このクラスのオブジェクト (つまり) を提供するときに、クラスがOriginalClassあり、それが実装されます。私の理解が正しければ、次の質問に答えていただけますかOriginalInterfaceOriginalInterfaceProxyClassProxyClass

  1. ProxyClassクラスにキャストオブジェクトを入力すると、OriginalClassこれは機能しますが、Java はこれをどのように許可していますか? のインスタンスの場合も同じですか?
  2. 私の知る限り、Java はメソッドのみでプロキシ クラスを作成しますが、このオブジェクトの属性にアクセスしようとするとどうなりますか?
  3. インターフェイスメソッドのみがプロキシに実装されていますが、インターフェイスになく、クラスでのみ言及されているメソッドにアクセスしようとするとどうなりますか?

ありがとう、学生

4

2 に答える 2

11

Java は、プロキシから具象クラスへのキャストを許可していません。JDK プロキシ ( java.lang.reflect.Proxy) は、インターフェースの単なるプロキシです。結果のプロキシはタイプProxyX(X は数値) であり、それを任意のクラスにキャストしようとすると、次のようになります。ClassCastException

したがって、2 番目と 3 番目の質問は関係ありません。プロキシは具体的なクラスに支えられていません。これを実現するには、他のプロキシ メカニズム (CGLIB または javassist) を使用できます。それらは動的サブクラス化を使用するため、すべてのprotected(およびそれ以上の) フィールドとメソッドはサブクラス (プロキシ) からアクセスできます。

于 2010-05-03T10:33:57.600 に答える
8

java.lang.reflect.InvocationHandler の API javadoc から:

InvocationHandler は、プロキシ インスタンスの呼び出しハンドラによって実装されるインターフェイスです。

動的プロキシはインターフェースを実装しますが、ハンドラー (OriginalClass) を使用してメソッドの基本実装を提供します。

質問に答えるには:

  1. コンパイラは、キャストが成功しないことを確認するのに十分な情報がない限り、キャストを許可します。動的プロキシのキャストおよび instanceof テストの実行時の動作については、java.lang.reflect.Proxy の javadoc で説明されています。キャストと instanceof テストは、インターフェイスで使用すると成功しますが、クラスで使用すると成功しません。
  2. 動的プロキシはインターフェイスを実装し、ハンドラ クラスを拡張しないため、動的プロキシを使用して属性にアクセスすることはできません。
  3. 動的プロキシを使用してインターフェイスで宣言されていないメソッドにアクセスすることはできません。これは、インターフェイスを実装し、ハンドラー クラスを拡張しないためです。

動的プロキシの実装内 (たとえば、invoke(...) メソッドの実装内) では、リフレクションを使用してハンドラーのメンバーにアクセスできます。

回答を確認するために使用したテストコードを次に示します。

// package ...;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import junit.framework.Assert;

import org.junit.Test;

public class TestDynamicProxy
{
    @Test
    public void testCast() throws Exception {
        Foo foo = (Foo) TestProxy.newInstance(new FooImpl());
        foo.bar(null);

        System.out.println("Class: " + foo.getClass());
        System.out.println("Interfaces: " + foo.getClass().getInterfaces());

        Assert.assertNotNull(foo);
        Assert.assertTrue(foo instanceof Foo);
        Assert.assertFalse(foo instanceof FooImpl);
    }
}

interface Foo
{
    Object bar(Object obj) throws Exception;
}

class FooImpl implements Foo
{
    public Object bar(Object obj) throws Exception {
        return null;
    }
}

class TestProxy implements java.lang.reflect.InvocationHandler
{
    private final Object obj;

    public static Object newInstance(Object obj) {
        return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new TestProxy(obj));
    }

    private TestProxy(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result;

        try {
            result = m.invoke(obj, args);
        }
        catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
        catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
        }

        return result;
    }
}

この記事には、役立つ情報とサンプル コードが多数含まれています。

于 2010-05-03T10:37:02.813 に答える