模块 java.base

类 Proxy

java.lang.Object
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);
  

proxy class 是在运行时创建的类,它实现了指定的接口列表,称为 proxy interfacesproxy instance 是代理类的一个实例。每个代理实例都有一个关联的调用处理程序对象,它实现了接口 InvocationHandler 。通过其代理接口之一对代理实例的方法调用将被分派到实例调用处理程序的 invoke 方法,传递代理实例、标识被调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型数组.调用处理程序适当地处理编码的方法调用,并且它返回的结果将作为对代理实例的方法调用的结果返回。

代理类具有以下属性:

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

代理实例具有以下属性:

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

代理类的包和模块成员

选择代理类所属的包和模块,使得代理类的可访问性与代理接口的可访问性一致。具体来说,通过 getProxyClass(ClassLoader, Class[]) newProxyInstance(ClassLoader, Class[], InvocationHandler) 方法定义的代理类的包和模块成员指定如下:
  1. 如果所有代理接口都在 exportedopen 包中:
    1. 如果所有代理接口都是 public ,那么代理类是 public 在无条件导出但未打开的包中。包和模块的名称未指定。
    2. 如果所有代理接口中至少有一个是 non-public ,则代理类在非公共接口的包和模块中是 non-public 。所有非公共接口必须在同一个包和模块中;否则,代理它们是 不可能
  2. 如果至少一个代理接口位于 non-exportednon-open 的包中:
    1. 如果所有代理接口都是 public ,那么代理类是 non-exportednon-open 包中的 dynamic module . 包和模块的名称是未指定的。
    2. 如果所有代理接口中至少有一个是 non-public ,则代理类在非公共接口的包和模块中是 non-public 。所有非公共接口必须在同一个包和模块中;否则,代理它们是 不可能

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

请注意,任意代码都可以使用 setAccessible 访问开放包中的代理类,而非开放包中的代理类永远无法被代理类模块外部的代码访问。

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

动态模块

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

动态模块可以读取代理类的所有超接口的模块以及代理类的所有公共方法签名引用的类和接口的模块。如果超级接口或引用的类或接口,比如 T,在非导出包中,则更新 Tmodule 以将 T 的包导出到动态模块。

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

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

如果代理接口包含与 java.lang.ObjecthashCodeequalstoString 方法具有相同名称和参数签名的方法,则在代理实例上调用此类方法时,传递给调用处理程序的 Method 对象将具有 java.lang.Object 作为其声明类。换句话说,java.lang.Object 的公共非最终方法在逻辑上先于所有代理接口,以确定将哪个 Method 对象传递给调用处理程序。

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

自从:
1.3
参见:
  • 字段详细信息

  • 构造方法详细信息

    • 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 对象。代理类将由指定的类加载器定义,并将实现所有提供的接口。如果任何给定接口是非公共的,则代理类将是非公共的。如果类加载器已经定义了用于相同接口排列的代理类,则将返回现有的代理类;否则,这些接口的代理类将动态生成并由类加载器定义。
      参数:
      loader - 定义代理类的类加载器
      interfaces - 代理类要实现的接口列表
      返回:
      在指定类加载器中定义并实现指定接口的代理类
      抛出:
      IllegalArgumentException - 如果违反参数中的任何 限制
      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 数组中没有两个元素可以引用相同的 Class 对象。
      • 通过指定的类加载器,所有接口类型都必须按名称可见。换句话说,对于类加载器 cl 和每个接口 i ,以下表达式必须为真:

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

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

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

      参数:
      loader - 定义代理类的类加载器
      interfaces - 代理类要实现的接口列表
      h - 将方法调用分派到的调用处理程序
      返回:
      具有由指定类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例
      抛出:
      IllegalArgumentException - 如果违反参数中的任何 限制
      SecurityException - 如果存在安全管理器 s 并且满足以下任何条件:
      • 给定的 loadernull 并且调用者的类加载器不是 null 并且使用 RuntimePermission("getClassLoader") 权限调用 s.checkPermission 拒绝访问;
      • 对于每个代理接口 intf ,调用者的类加载器与 intf 的类加载器不同或不是其祖先,调用 s.checkPackageAccess() 拒绝访问 intf
      • 任何给定的代理接口都是非公共的,调用者类与非公共接口不在同一个 运行时包 中,并且使用 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() 的调用拒绝访问调用处理程序的类。