Module  java.base
软件包  java.lang.reflect

Class Proxy

  • All Implemented Interfaces:
    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
    • 代理类是最终的和非抽象的
    • 代理类扩展为java.lang.reflect.Proxy
    • 代理类完全按照相同的顺序实现其创建时指定的接口。 调用getInterfaces其上Class对象将返回包含接口的同一列表的阵列(在其创建时指定的顺序),调用getMethods其上Class对象将返回的数组方法对象,其中包括所有的在这些接口的方法,以及调用getMethod将会在代理接口中找到可以预期的方法。
    • 代理类的ProtectionDomain与引导类加载器加载的系统类(例如java.lang.Object ,因为代理类的代码是由可信系统代码生成的。 这个保护域通常会被授予java.security.AllPermission
    • 可以使用Proxy.isProxyClass方法来确定给定的类是否是代理类。

    代理实例具有以下属性:

    • 给定代理实例proxy和由其代理类实现的接口Foo ,以下表达式将返回true:
        proxy instanceof Foo 
      并且以下的投射操作将会成功(而不是抛出一个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的公共非最终方法逻辑上先于所有的代理接口,以确定哪个方法对象传递给调用处理程序。

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

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

      • Proxy

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

      • 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 - 如果任何 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,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
        返回指定接口的代理实例,该代理实例将方法调用分派给指定的调用处理程序。

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

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

          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 ,或者如果调用处理程序( h )为 null
        另请参见:
        Package and Module Membership of Proxy Class
      • isProxyClass

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

        public static InvocationHandler getInvocationHandler​(Object proxy)
                                                      throws IllegalArgumentException
        返回指定代理实例的调用处理程序。
        参数
        proxy - 用于返回调用处理程序的代理实例
        结果
        代理实例的调用处理程序
        异常
        IllegalArgumentException - 如果参数不是代理实例
        SecurityException - if a security manager, s, is present and the caller's class loader is not the same as or an ancestor of the class loader for the invocation handler and invocation of s.checkPackageAccess() denies access to the invocation handler's class.