モジュール java.base
パッケージ java.lang.reflect

クラスProxy

  • すべての実装されたインタフェース:
    Serializable

    public class Proxy
    extends Object
    implements Serializable
    Proxyは、インタフェースのインスタンスのように動作するが、カスタマイズされたメソッド呼び出しを可能にするオブジェクトを作成する静的メソッドを提供します。 インタフェースFooのプロキシ・インスタンスを作成するには:
    
         InvocationHandler handler = new MyInvocationHandler(...);
         Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                              new Class<?>[] { Foo.class },
                                              handler);
     

    「プロキシ・クラス」は、実行時に作成され、「プロキシ・インタフェース」と呼ばれる指定されたインタフェースのリストを実装するクラスです。 プロキシ・インスタンスは、プロキシ・クラスのインスタンスです。 各プロキシ・インスタンスには関連付けられた呼出しハンドラ・オブジェクトがあり、それにはインタフェースInvocationHandlerが実装されています。 プロキシ・インタフェースの1つを使ったプロキシ・インスタンスでのメソッド呼出しは、インスタンスの呼出しハンドラのinvokeメソッドにディスパッチされ、呼び出されたメソッドを識別するjava.lang.reflect.Methodオブジェクト、および引数を格納するObject型の配列をプロキシ・インスタンスに引き渡します。 呼出しハンドラは符号化されたメソッド呼出しを適切に処理し、呼出しハンドラが返す結果が、プロキシ・インスタンスでのメソッド呼出しの結果として返されます。

    プロキシ・クラスには次のプロパティがあります。

    • プロキシ・クラスの修飾されていない名前は指定されません。 ただし文字列$Proxyで始まるクラス名の領域をプロキシ・クラスのために確保しておく必要があります。
    • プロキシ・クラスが定義されているパッケージおよびモジュールは、belowで指定します。
    • プロキシ・クラスは「最終的かつ非抽象的」です。
    • プロキシ・クラスはjava.lang.reflect.Proxyを継承します。
    • プロキシ・クラスはその生成時に指定されたとおりのインタフェースを、同じ順序で実装します。 そのClassオブジェクト上getMethods呼び出し、インタフェース(その作成時に指定された順序で)の同じリストを含む配列が返され、そのClassオブジェクト上getInterfaces呼び出すと、それらのインタフェースのメソッドのすべてを含むMethodオブジェクトの配列を返し、getMethodを起動すると、プロキシのメソッドを見つけますインタフェースが期待されるようになります。
    • プロキシ・クラスのProtectionDomainは、信頼できるシステム・コードによってプロキシ・クラスのコードが生成されるため、ブートストラップ・クラス・ローダーによってロードされるシステム・クラスのProtectionDomain(java.lang.Objectなど)と同じです。 標準では、この保護ドメインに対してjava.security.AllPermissionが与えられます。
    • Proxy.isProxyClassメソッドを使用して、指定されたクラスがプロキシ・クラスであるかどうかを判断できます。

    プロキシ・インスタンスには次のプロパティがあります。

    • プロキシ・インスタンスproxyと、プロキシ・クラスによって実装されたインタフェースFooの1つが指定されている場合、次の式はtrueを返します:
           proxy instanceof Foo
       
      また、次のキャスト操作が成功します(ClassCastExceptionをスローする場合を除く)。
           (Foo) proxy
       
    • 各プロキシ・インスタンスには、そのコンストラクタに引き渡された、関連する呼出しハンドラがあります。 static Proxy.getInvocationHandlerメソッドは、その引数として渡されたプロキシ・インスタンスに関連付けられた呼出しハンドラを返します。
    • プロキシ・インスタンスでのインタフェース・メソッドの呼出しは、そのメソッドのドキュメントで述べられているように符号化され、呼出しハンドラのinvokeメソッドにディスパッチされます。
    • プロキシ・インスタンス上のjava.lang.Objectに宣言されているhashCodeequalsまたはtoStringの呼出しは、前述したようにインタフェース・メソッド呼出しと同じ方法で、エンコードされ、呼出しハンドラのinvokeメソッドにディスパッチされます。 invokeに渡されるMethodオブジェクトの宣言クラスは、java.lang.Objectです。 java.lang.Objectから継承されるプロキシ・インスタンスのその他のpublicメソッドは、プロキシ・クラスによってオーバーライドされません。このため、これらのメソッドの呼出しは、java.lang.Objectのインスタンスに対する呼出しと同様に行われます。

    プロキシ・クラスのパッケージとモジュールのメンバーシップ

    プロキシ・クラスが属するパッケージおよびモジュールは、プロキシ・クラスのアクセシビリティがプロキシ・インタフェースのアクセシビリティと一致するように選択されます。 具体的には、getProxyClass(ClassLoader, Class[])またはnewProxyInstance(ClassLoader, Class[], InvocationHandler)メソッドで定義されたプロキシ・クラスのパッケージおよびモジュール・メンバーシップは、次のように指定されます:
    1. すべてのプロキシ・インタフェースがexportedまたはopenパッケージにある場合は、次のようになります:
      1. すべてのプロキシ・インタフェースがpublicの場合、プロキシ・クラスは、指定されたローダーの「名前のないモジュール」によってエクスポートされたパッケージ内のpublicです。 パッケージ名は不定です。
      2. すべてのプロキシ・インタフェースの少なくとも1つがnon-publicである場合、プロキシ・クラスは非パブリック・インタフェースのパッケージおよびモジュール内のnon-publicです。 すべての非公開インタフェースは、同じパッケージとモジュールになければなりません。それ以外の場合、プロキシは「ありえない」です。
    2. non-exportedおよびnon-openであるパッケージに少なくとも1つのプロキシ・インタフェースがある場合:
      1. すべてのプロキシ・インタフェースがpublicの場合、プロキシ・クラスは動的モジュール。」non-exportednon-openパッケージのpublicです。パッケージとモジュールの名前は指定されていません。
      2. すべてのプロキシ・インタフェースの少なくとも1つがnon-publicである場合、プロキシ・クラスは非パブリック・インタフェースのパッケージおよびモジュール内のnon-publicです。 すべての非公開インタフェースは、同じパッケージとモジュールになければなりません。それ以外の場合、プロキシは「ありえない」です。

    プロキシ・インタフェースが複数の機能を備えている場合-- たとえば、エクスポートされたパブリック・インタフェースとエクスポートされていない非パブリック・インタフェース-- 同じインスタンスによってプロキシされる場合、プロキシ・クラスのアクセシビリティは、最もアクセスしにくいプロキシ・インタフェースによって管理されます。

    任意のコードがsetAccessibleを使用してオープン・パッケージ内のプロキシ・クラスにアクセスすることは可能ですが、非オープン・パッケージ内のプロキシ・クラスはプロキシ・クラスのモジュール外でコードにアクセスすることはできません。

    この仕様では、"エクスポートされていないパッケージ"はすべてのモジュールにエクスポートされていないパッケージを指し、"非オープン・パッケージ"はすべてのモジュールに対して開かれていないパッケージを指します。 具体的には、これらの用語は、その包含モジュールによってエクスポート/オープンされないか、またはその包含モジュールによって限定された方法でエクスポート/オープンされるパッケージを指す。

    ダイナミック・モジュール

    動的モジュールは、実行時に生成される名前付きモジュールです。 ダイナミック・モジュールで定義されたプロキシ・クラスはカプセル化されており、どのモジュールからもアクセスできません。 動的モジュールのプロキシ・クラスでConstructor.newInstance(Object...)を呼び出すと、IllegalAccessExceptionがスローされます。代わりにProxy.newProxyInstanceメソッドを使用する必要があります。

    動的モジュールは、プロキシ・クラスのすべてのスーパー・インタフェースのモジュールと、プロキシ・クラスのすべてのパブリック・メソッド・シグネチャによって参照される型のモジュールを読み取ることができます。 スーパー・インタフェースまたは参照される型、たとえばTがエクスポートされていないパッケージにある場合、Tmoduleが更新され、Tのパッケージが動的モジュールにエクスポートされます。

    複数のプロキシ・インタフェースで重複するメソッド

    2つ以上のプロキシ・インタフェースに同じ名前とパラメータ・シグネチャを持つメソッドが含まれている場合、プロキシ・クラス・インタフェースの順序が重要になります。 プロキシ・インスタンス上で重複するメソッドが呼び出された場合、呼出しハンドラに渡されるMethodオブジェクトで、プロキシ・メソッドの呼出しに使用されたインタフェースの参照型から宣言クラスを割り当てることができないことがあります。 このような制約が存在するのは、生成されたプロキシ・クラス内の対応するメソッドの実装から、その実装が呼び出されたときに使用されたインタフェースを特定できないためです。 このため、プロキシ・インスタンス上で重複するメソッドが呼び出された場合は、メソッド呼出しに使用された参照型にかかわりなく、プロキシ・クラスのインタフェース・リストでそのメソッド(直接またはスーパー・インタフェースから継承)を含むインタフェースのうち、最初のインタフェースのメソッドのMethodオブジェクトが呼出しハンドラのinvokeメソッドに渡されます。

    プロキシ・インタフェースに、java.lang.ObjecthashCodeequals、またはtoStringメソッドと同じ名前およびパラメータ・シグニチャを持つメソッドが含まれる場合は、プロキシ・インスタンス上でそのメソッドが呼び出されると、呼出しハンドラに渡されるMethodオブジェクトの宣言クラスはjava.lang.Objectになります。 つまり、publicで非finalであるjava.lang.Objectのメソッドは、呼出しハンドラに渡すMethodオブジェクトを決定するときに、論理的にほかのプロキシ・インタフェースより優先されます。

    重複するメソッドが呼出しハンドラにディスパッチされた場合は、invokeメソッドからスローできるチェック例外の型は、チェックされる型のうち、呼び出されるすべてのプロキシ・インタフェースのメソッドに指定されている、throws句の例外の型に割り当てることができるものに限定されます。 invokeメソッドが、呼出しに使えるプロキシ・インタフェースの1つのメソッドで宣言された例外タイプのどれにも割当てできないチェック例外をスローした場合、チェックされないUndeclaredThrowableExceptionがプロキシ・インスタンスでの呼出しによってスローされます。 つまり、invokeメソッドに渡されたMethodオブジェクト上で、getExceptionTypesを呼び出して例外の型を取得しても、invokeメソッドから正常にスローされないことがあります。

    導入されたバージョン:
    1.3
    関連項目:
    InvocationHandler, 直列化された形式
    • フィールドの詳細

      • h

        protected InvocationHandler h
        このプロキシ・インスタンスの呼出しハンドラです。
    • コンストラクタの詳細

      • Proxy

        protected Proxy​(InvocationHandler h)
        指定された値で、サブクラス(通常は動的プロキシ・クラス)からその呼出しハンドラに新しいProxyインスタンスを構築します。
        パラメータ:
        h - このプロキシ・インスタンスの呼出しハンドラ
        例外:
        NullPointerException - 指定された呼び出しハンドラhnullの場合
    • メソッドの詳細

      • getProxyClass

        @Deprecated
        public static Class<?> getProxyClass​(ClassLoader loader,
                                             Class<?>... interfaces)
                                      throws IllegalArgumentException
        非推奨。
        名前付きモジュールで生成されたプロキシ・クラスはカプセル化され、モジュール外のコードにはアクセスできません。 Constructor.newInstanceは、アクセスできないプロキシ・クラスで呼び出されたときにIllegalAccessExceptionをスローします。 代わりにnewProxyInstance(ClassLoader, Class[], InvocationHandler)を使用してプロキシ・インスタンスを作成してください。
        クラス・ローダーとインタフェースの配列の指定されたプロキシ・クラスのjava.lang.Classオブジェクトを返します。 プロキシ・クラスは指定されたクラス・ローダーにより定義され、指定されたインタフェースをすべて実装します。 指定されたインタフェースのいずれかが非publicである場合、プロキシ・クラスは非publicになります。 インタフェースの同じ順列のプロキシ・クラスがすでにクラス・ローダーにより定義されている場合、既存のプロキシ・クラスが返されます。そうでない場合は、これらのインタフェースのプロキシ・クラスが動的に生成され、クラス・ローダーにより定義されます。
        パラメータ:
        loader - プロキシ・クラスを定義するクラス・ローダー
        interfaces - プロキシ・クラスが実装するインタフェースのリスト
        戻り値:
        指定されたクラス・ローダーで定義され、指定されたインタフェースを実装するプロキシ・クラス
        例外:
        IllegalArgumentException - パラメータ上のrestrictionsのいずれかに違反した場合
        SecurityException - セキュリティ・マネージャsが存在し、次の条件のどれかが満たされる場合:
        • 指定されたloadernullで、呼出し側のクラス・ローダーがnullではなく、RuntimePermission("getClassLoader")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
        • 各プロキシ・インタフェースintfについて、呼出し側のクラス・ローダーがintfのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しがintfへのアクセスを許可しない。
        NullPointerException - interfaces配列の引数またはその要素のいずれかがnullの場合
        関連項目:
        プロキシ・クラスのパッケージとモジュールのメンバーシップ
      • newProxyInstance

        public static Object newProxyInstance​(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
        メソッドの呼び出しを指定された呼び出しハンドラにディスパッチする、指定されたインタフェースのプロキシ・インスタンスを返します。

        次の制限のいずれかに違反すると、IllegalArgumentExceptionがスローされます:

        • 指定されたinterfaces配列内のClassオブジェクトはすべて、クラスまたはプリミティブ型ではなく、インタフェースを表す必要があります。
        • interfaces配列の2つの要素が同一のClassオブジェクトを参照することはできない。
        • すべての型のインタフェースは、対応するクラス・ローダーから名前で参照できなければならない。 つまり、クラス・ローダーがcl、各インタフェースがiの場合は、次の式がtrueでなければならない。

          Class.forName(i.getName(), false, cl) == i

        • 指定されたインタフェースのすべてのパブリック・メソッド・シグネチャによって参照される型およびスーパー・インタフェースによって継承される型はすべて、指定されたクラス・ローダーを介して名前で表示される必要があります。
        • すべての非公開インタフェースは、指定されたクラス・ローダーによって定義された同じパッケージおよびモジュール内になければならず、非公開インタフェースのモジュールはすべてのインタフェース型にアクセスできます。そうしないと、プロキシ・クラスが定義されているパッケージに関係なく、すべてのインタフェースを実装することができなくなります。
        • 指定されたインタフェースが同じ署名を持つ場合、そのようなメソッドの任意の組に対して
          • 任意のメソッドの戻り値の型がプリミティブ型またはvoidの場合、それらのメソッドはすべて戻り値の型が同じでなければならない
          • そうでない場合、いずれかのメソッドによる戻り値の型は、残りのメソッドによる戻り値の型すべてに対して割当て可能でなければならない
        • プロキシ・クラスを作成するときは、Virtual Machineに定義されているクラスの制限を超えてはならない。 たとえば、VMが、クラスが実装できるインタフェース数を65535に制限している場合は、interfaces配列のサイズは65535を超えてはならない。

        プロキシ・インタフェースは、順番が区別されます。プロキシ・クラスを2回要求したときに、インタフェースの組み合わせが同じで順番が異なる場合は、2つの異なるプロキシ・クラスが作成されます。

        パラメータ:
        loader - プロキシ・クラスを定義するクラス・ローダー
        interfaces - プロキシ・クラスが実装するインタフェースのリスト
        h - メソッド呼出しのディスパッチ先の呼出しハンドラ
        戻り値:
        指定されたクラス・ローダーで定義され、指定されたインタフェースを実装するプロキシ・クラスの、指定された呼出しハンドラを持つプロキシ・インスタンス
        例外:
        IllegalArgumentException - パラメータ上のrestrictionsのいずれかに違反した場合
        SecurityException - セキュリティ・マネージャsが存在し、次の条件のどれかが満たされる場合:
        • 指定されたloadernullで、呼出し側のクラス・ローダーがnullではなく、RuntimePermission("getClassLoader")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
        • 各プロキシ・インタフェースintfについて、呼出し側のクラス・ローダーがintfのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しがintfへのアクセスを許可しない。
        • 指定されたプロキシ・インタフェースのいずれかが非publicであり、呼出し側のクラスが非publicインタフェースと同じ実行時パッケージ内にはなく、ReflectPermission("newProxyInPackage.{package name}")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
        NullPointerException - interfaces配列の引数またはその要素のいずれかがnullの場合、または呼出しハンドラhnullの場合
        関連項目:
        プロキシ・クラスのパッケージとモジュールのメンバーシップ
      • isProxyClass

        public static boolean isProxyClass​(Class<?> cl)
        指定されたクラスがプロキシ・クラスの場合にtrueを返します。
        実装上の注意:
        セキュリティを判定するときにこのメソッドを使用する場合は、信頼性が重要になります。このため、渡されたクラスがProxyを継承しているかどうかを検査してから、追加の検査を行う必要があります。
        パラメータ:
        cl - テストするクラス
        戻り値:
        クラスがプロキシ・クラスの場合はtrue、そうでない場合はfalse
        例外:
        NullPointerException - clnullである場合
      • getInvocationHandler

        public static InvocationHandler getInvocationHandler​(Object proxy)
                                                      throws IllegalArgumentException
        指定されたプロキシ・インスタンスの呼出しハンドラを返します。
        パラメータ:
        proxy - 呼出しハンドラを返すプロキシ・インスタンス
        戻り値:
        プロキシ・インスタンスの呼出しハンドラ
        例外:
        IllegalArgumentException - 引数がプロキシ・インスタンスではない場合
        SecurityException - セキュリティ・マネージャsが存在し、呼出し側のクラス・ローダーが呼出しハンドラのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しが呼出しハンドラのクラスのアクセスを許可しない。