模块 java.base

接口 InvocationHandler

所有已知的实现类:
CompositeDataInvocationHandler , EventHandler , MBeanServerInvocationHandler , RemoteObjectInvocationHandler

public interface InvocationHandler
InvocationHandler是接口实现的调用处理程序一个代理实例。

每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用被编码并分派到其调用处理程序的 invoke 方法。

自从:
1.3
参见:
  • 方法总结

    修饰符和类型
    方法
    描述
    invoke(Object proxy, Method method, Object[] args)
    处理代理实例上的方法调用并返回结果。
    static Object
    invokeDefault(Object proxy, Method method, Object... args)
    使用给定的参数在给定的 proxy 实例上调用指定的默认方法。
  • 方法详情

    • invoke

      Object  invoke(Object  proxy, Method  method, Object [] args) throws Throwable
      处理代理实例上的方法调用并返回结果。当在与其关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
      参数:
      proxy - 调用该方法的代理实例
      method - 与在代理实例上调用的接口方法对应的 Method 实例。 Method 对象的声明类将是声明该方法的接口,它可能是代理类通过其继承该方法的代理接口的超接口。
      args - 包含在代理实例的方法调用中传递的参数值的对象数组,或者 null 如果接口方法不带参数。原始类型的参数包装在适当的原始包装类的实例中,例如 java.lang.Integerjava.lang.Boolean
      返回:
      从代理实例上的方法调用返回的值。如果接口方法声明的返回类型是原始类型,则该方法返回的值必须是对应原始包装类的实例;否则,它必须是可分配给声明的返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型为原始类型,则代理实例上的方法调用将抛出 NullPointerException。如果此方法返回的值与上述接口方法声明的返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException
      抛出:
      Throwable - 从代理实例的方法调用中抛出的异常。异常的类型必须可分配给接口方法的 throws 子句中声明的任何异常类型或未检查的异常类型 java.lang.RuntimeExceptionjava.lang.Error 。如果此方法抛出一个不可分配给接口方法的 throws 子句中声明的任何异常类型的已检查异常,则包含此方法抛出的异常的 UndeclaredThrowableException 将由方法调用抛出代理实例。
      参见:
    • invokeDefault

      static Object  invokeDefault(Object  proxy, Method  method, Object ... args) throws Throwable
      使用给定的参数在给定的 proxy 实例上调用指定的默认方法。给定的 method 必须是在 proxy 类的代理接口中声明的默认方法,或者直接或间接继承自其超接口。

      调用此方法的行为就像从代理类执行 invokespecial 指令一样,以代理接口中的默认方法为目标。这等效于调用:X.super.m(A* a),其中 X 是代理接口,对 X.super::m(A*) 的调用解析为给定的 method

      示例:接口 AB 都声明了方法 m 的默认实现。接口 C 扩展了 A 并从其超接口 A 继承了默认方法 m

      
       interface A {
         default T m(A a) { return t1; }
       }
       interface B {
         default T m(A a) { return t2; }
       }
       interface C extends A {}
        
      下面创建一个实现 A 并调用默认方法 A::m 的代理实例。
      
       Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class },
           (o, m, params) -> {
             if (m.isDefault()) {
               // if it's a default method, invoke it
               return InvocationHandler.invokeDefault(o, m, params);
             }
           });
        
      如果代理实例同时实现 AB ,它们都提供方法 m 的默认实现,调用处理程序可以通过 invokeDefault 方法将方法调用分派给 A::mB::m。例如,以下代码将方法调用委托给 B::m
      
       Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class },
           (o, m, params) -> {
             if (m.getName().equals("m")) {
               // invoke B::m instead of A::m
               Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes());
               return InvocationHandler.invokeDefault(o, bMethod, params);
             }
           });
        
      如果代理实例实现从其超接口 A 继承默认方法 mC,则 "m" 上的接口方法调用将分派到调用处理程序的 invoke 方法,其中 Method 对象参数代表默认方法 A::m
      
       Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
          (o, m, params) -> {
             if (m.isDefault()) {
               // behaves as if calling C.super.m(params)
               return InvocationHandler.invokeDefault(o, m, params);
             }
          });
        
      在这个 proxy 上调用方法 "m" 的行为就像调用 C.super::m 并且解析为调用 A::m 一样。

      如果现有代码试图调用 invokeDefault 以调用默认方法,则添加默认方法或将方法从抽象更改为默认可能会导致异常。例如,如果修改 C 以实现默认方法 m

      
       interface C extends A {
         default T m(A a) { return t3; }
       }
        
      上面使用修改后的 C 创建代理实例 proxy 的代码将无一例外地运行,并将导致调用 C::m 而不是 A::m

      以下是创建 C 的代理实例的另一个示例,调用处理程序调用 invokeDefault 方法来调用 A::m

      
       C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
           (o, m, params) -> {
             if (m.getName().equals("m")) {
               // IllegalArgumentException thrown as {@code A::m} is not a method
               // inherited from its proxy interface C
               Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes());
               return InvocationHandler.invokeDefault(o, aMethod params);
             }
           });
       c.m(...);
        
      上面的代码使用旧版本的 C 成功运行,并调用了 A::m。当使用新版本的 C 运行时,上述代码将因 IllegalArgumentException 而失败,因为 C 覆盖了相同方法的实现,而代理实例无法访问 A::m
      API 注意:
      proxy 参数的类型为 Object 而不是 Proxy 以便于 InvocationHandler::invoke 实现无需强制转换即可直接调用。
      参数:
      proxy - 要在其上调用默认方法的 Proxy 实例
      method - 对应于在代理类的代理接口中声明或直接或间接从其超接口继承的默认方法的 Method 实例
      args - 用于方法调用的参数;如果该方法所需的形式参数的数量为零,则可以是 null
      返回:
      从方法调用返回的值
      抛出:
      IllegalArgumentException - 如果以下任何条件为 true
      • proxy 不是 代理实例;或者
      • 给定的 method 不是在代理类的代理接口中声明的默认方法,也不是从其任何超接口继承的;或者
      • 给定的 method 被代理接口直接或间接覆盖,并且对命名方法的方法引用永远不会解析为给定的 method ;或者
      • 给定的 args 数组的长度与要调用的方法的参数数量不匹配;或者
      • 如果相应的方法参数类型是原始类型,则任何 args 元素都无法进行拆箱转换;或者,如果在可能的拆箱之后,无法将任何 args 元素分配给相应的方法参数类型。
      IllegalAccessException - 如果调用者类无法访问指定默认方法的声明类
      NullPointerException - 如果 proxymethodnull
      Throwable - 默认方法抛出的任何东西
      Java 虚拟机规范:
      5.4.3.方法解析
      自从:
      16