模块  java.base
软件包  java.lang.reflect

Class 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 通过其代理接口之一对代理实例的方法调用将被分派到实例的调用处理程序的invoke方法,传递代理实例,标识被调用方法的java.lang.reflect.Method对象,以及包含参数的类型Object的数组。 调用处理程序适当地处理编码的方法调用,并且它返回的结果将作为代理实例上的方法调用的结果返回。

    代理类具有以下属性:

    • 未指定代理类的非限定名称。 但是,以字符串"$Proxy"开头的类名空间应该保留给代理类。
    • 定义代理类的包和模块指定为below
    • 代理类是final和非抽象的
    • 代理类扩展java.lang.reflect.Proxy
    • 代理类以相同的顺序实现其创建时指定的接口。 在其对象上调用getInterfaces将返回包含相同接口列表的数组(按创建时指定的顺序),在其对象上调用getMethods将返回包含这些接口中所有方法的方法对象的数组,以及调用getMethod会在代理接口中找到预期的方法。
    • 代理类的ProtectionDomain与引导类加载器加载的系统类(例如java.lang.Object ,因为代理类的代码由受信任的系统代码生成。 该保护域通常将被授予java.security.AllPermission
    • Proxy.isProxyClass方法可用于确定给定的类是否是代理类。

    代理实例具有以下属性:

    • 给定代理实例proxy和由其代理类实现的其中一个接口Foo ,以下表达式将返回true:
        proxy instanceof Foo 
      并且以下ClassCastException操作将成功(而不是抛出ClassCastException ):
        (Foo) proxy 
    • 每个代理实例都有一个关联的调用处理程序,即传递给其构造函数的处理程序。 静态Proxy.getInvocationHandler方法将返回与作为其参数传递的代理实例关联的调用处理程序。
    • 代理实例上的接口方法调用将被编码并调度到调用处理程序的invoke方法,如该方法的文档中所述。
    • 的调用hashCodeequals ,或toString中声明的方法java.lang.Object上的代理实例将被编码并分派到调用处理程序的invoke中相同的方式方法,接口方法调用进行编码和调度,如上所述。 传递给invoke方法对象的声明类将为java.lang.Object java.lang.Object继承的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用行为与java.lang.Object实例java.lang.Object

    Package and Module Membership of Proxy Class

    选择代理类所属的包和模块,使得代理类的可访问性与代理接口的可访问性一致。 具体而言,通过getProxyClass(ClassLoader, Class[])newProxyInstance(ClassLoader, Class[], InvocationHandler)方法定义的代理类的包和模块成员资格指定如下:
    1. 如果所有代理接口都在导出打开的包中:
      1. 如果所有代理接口都是公共的 ,则代理类在指定加载器的unnamed module导出的包中是公共的 包的名称未指定。
      2. 如果所有的代理接口的至少一个是非公 ,则代理类是在非公共接口的包和模块的非公开 所有非公共接口必须在同一个包和模块中; 否则,代理他们是not possible
    2. 如果至少有一个代理接口位于未导出且未 打开的包中
      1. 如果所有代理接口都是公共的 ,则代理类在未导出的 非打开包中是公共dynamic module.未指定包和模块的名称。
      2. 如果所有的代理接口的至少一个是非公 ,则代理类是在非公共接口的包和模块的非公开 所有非公共接口必须在同一个包和模块中; 否则,代理他们是not possible

    请注意,如果具有混合可访问性的代理接口(例如,导出的公共接口和非导出的非公共接口)由同一实例代理,则代理类的可访问性由最不可访问的代理接口控制。

    请注意,任意代码都可以使用setAccessible获取对打开包中的代理类的访问权限,而非开放包中的代理类永远不会被代理类模块外部的代码访问。

    在整个说明书中,“未导出的包”是指未导出到所有模块的包,“非打开包”是指不对所有模块开放的包。 具体来说,这些术语指的是一个包,它不是由其包含的模块导出/打开,也不是由其包含的模块以合格的方式导出/打开。

    Dynamic Modules

    动态模块是在运行时生成的命名模块。 动态模块中定义的代理类被封装,任何模块都无法访问。 在动态模块中的代理类上调用Constructor.newInstance(Object...)将抛出IllegalAccessException ; 应该使用Proxy.newProxyInstance方法。

    动态模块可以读取代理类的所有超接口的模块以及代理类的所有公共方法签名所引用的类型的模块。 如果超级接口或引用类型(例如T )位于非导出包中,则更新TT以将包T导出到动态模块。

    方法在多个代理接口中重复

    当两个或多个代理接口包含具有相同名称和参数签名的方法时,代理类接口的顺序变得很重要。 在代理实例上调用此类重复方法时 ,传递给调用处理程序的方法对象不一定是其声明类可从调用代理方法的接口的引用类型分配的对象。 存在此限制是因为生成的代理类中的相应方法实现无法确定通过哪个接口调用它。 因此,当在代理实例上调用重复方法时,在代理类的接口列表中包含该方法(直接或通过超接口继承)的最前面接口中的方法的方法对象将传递给调用处理程序的invoke方法,无论方法调用发生的引用类型如何。

    如果代理接口包含具有相同的名称和参数签名的方法hashCodeequals ,或toString的方法java.lang.Object ,当这种方法在代理实例调用时, 方法传递到调用处理程序对象将java.lang.Object作为其宣布上课。 换句话说, java.lang.Object的公共非final方法逻辑上位于所有代理接口之前,用于确定将哪个方法对象传递给调用处理程序。

    另请注意,当将重复方法分派给调用处理程序时, invoke方法可能只会抛出可分配给可在其调用的所有代理接口中的方法的throws子句中的一个异常类型的已检查异常类型。通过。 如果invoke方法抛出一个已检查的异常,该异常不能分配给该方法在其中一个可以调用的代理接口中声明的任何异常类型,则代理实例上的调用将抛出未经检查的UndeclaredThrowableException 此限制意味着,并非所有通过调用返回的异常类型getExceptionTypes方法传递给对象invoke方法一定可以成功地抛出invoke方法。

    从以下版本开始:
    1.3
    另请参见:
    InvocationHandlerSerialized Form
    • 构造方法详细信息

      • Proxy

        protected Proxy​(InvocationHandler h)
        从子类(通常是动态代理类)构造一个新的 Proxy实例,并为其调用处理程序指定值。
        参数
        h - 此代理实例的调用处理程序
        异常
        NullPointerException - 如果给定的调用处理程序 hnull
    • 方法详细信息

      • getProxyClass

        @Deprecated
        public static <?> getProxyClass​(ClassLoader loader,
                                             <?>... interfaces)
                                      throws IllegalArgumentException
        Deprecated.
        Proxy classes generated in a named module are encapsulated and not accessible to code outside its module. Constructor.newInstance will throw IllegalAccessException when it is called on an inaccessible proxy class. Use newProxyInstance(ClassLoader, Class[], InvocationHandler) to create a proxy instance instead.
        给定类加载器和接口数组,返回代理类的java.lang.Class对象。 代理类将由指定的类加载器定义,并将实现所有提供的接口。 如果任何给定接口是非公共接口,则代理类将是非公共接口。 如果类加载器已经定义了相同的接口排列的代理类,那么将返回现有的代理类; 否则,将动态生成这些接口的代理类,并由类加载器定义。
        参数
        loader - 用于定义代理类的类加载器
        interfaces - 要实现的代理类的接口列表
        结果
        在指定的类加载器中定义并实现指定接口的代理类
        异常
        IllegalArgumentException - 如果违反了参数 restrictions中的任何一个
        SecurityException - 如果存在安全管理器s且满足以下任何条件:
        • 给定的loadernull ,调用者的类加载器不是null ,调用s.checkPermissionRuntimePermission("getClassLoader")权限拒绝访问。
        • 对于每个代理接口, intf ,调用者的类加载器与intf的类加载器或祖先不同,并且调用s.checkPackageAccess()拒绝访问intf
        NullPointerException - 如果 interfaces数组参数或其任何元素是 null
        另请参见:
        Package and Module Membership of Proxy Class
      • newProxyInstance

        public static Object newProxyInstance​(ClassLoader loader,
                                              <?>[] interfaces,
                                              InvocationHandler h)
        返回指定接口的代理实例,该接口将方法调用分派给指定的调用处理程序。

        IllegalArgumentException will be thrown if any of the following restrictions is violated:

        • 给定interfaces数组中的所有对象必须表示接口,而不是类或基元类型。
        • interfaces数组中没有两个元素可以引用相同的对象。
        • 所有接口类型必须通过指定的类加载器按名称可见。 换句话说,对于类加载器cl和每个接口i ,以下表达式必须为true:

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

        • 指定接口的所有公共方法签名引用的所有类型以及由其超接口继承的类型必须通过指定的类加载器按名称可见。
        • 所有非公共接口必须位于同一个包和模块中,由指定的类加载器定义,非公共接口的模块可以访问所有接口类型; 否则,代理类无法实现所有接口,无论它定义在哪个包中。
        • 对于具有相同签名的指定接口的任何成员方法集:
          • 如果任何方法的返回类型是基本类型或void,则所有方法必须具有相同的返回类型。
          • 否则,其中一个方法必须具有可分配给其余方法的所有返回类型的返回类型。
        • 生成的代理类不得超过虚拟机对类强加的任何限制。 例如,VM可以将类可以实现的接口数量限制为65535; 在这种情况下, interfaces阵列的大小不得超过65535。

        请注意,指定代理接口的顺序很重要:对具有相同接口组合但顺序不同的代理类的两个请求将导致两个不同的代理类。

        参数
        loader - 用于定义代理类的类加载器
        interfaces - 要实现的代理类的接口列表
        h - 调度方法调用的调用处理程序
        结果
        具有指定的代理类调用处理程序的代理实例,该代理类由指定的类加载器定义并实现指定的接口
        异常
        IllegalArgumentException - 如果违反了参数 restrictions中的任何一个
        SecurityException - 如果存在安全管理器s且满足以下任何条件:
        • 给定的loadernull ,调用者的类加载器不是null ,调用s.checkPermissionRuntimePermission("getClassLoader")权限拒绝访问;
        • 对于每个代理接口, intf ,调用者的类加载器与intf的类加载器或祖先不同,并且调用s.checkPackageAccess()拒绝访问intf ;
        • 任何给定的代理接口的是非公和呼叫者类是不在同一runtime package作为本次非公开接口的调用s.checkPermissionReflectPermission("newProxyInPackage.{package name}")权限拒绝访问。
        NullPointerException - 如果 interfaces数组参数或其任何元素是 null ,或者如果调用处理程序 hnull
        另请参见:
        Package and Module Membership of Proxy Class
      • isProxyClass

        public static boolean isProxyClass​(<?> cl)
        如果给定的类是代理类,则返回true。
        Implementation Note:
        此方法的可靠性对于使用它来做出安全决策的能力很重要,因此它的实现不应仅测试所讨论的类是否扩展为 Proxy
        参数
        cl - 要测试的类
        结果
        true如果类是代理类, false
        异常
        NullPointerException - 如果 clnull
      • getInvocationHandler

        public static InvocationHandler getInvocationHandler​(Object proxy)
                                                      throws IllegalArgumentException
        返回指定代理实例的调用处理程序。
        参数
        proxy - 用于返回调用处理程序的代理实例
        结果
        代理实例的调用处理程序
        异常
        IllegalArgumentException - 如果参数不是代理实例
        SecurityException - 如果存在安全管理器 s且调用者的类加载器与调用处理程序的类加载器的祖先或调用处理程序的祖先不同,则调用 s.checkPackageAccess()拒绝访问调用处理程序的类。