Module java.base

Interface InvocationHandler

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

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

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

自:
1.3
参见:
  • Method Summary

    Modifier and Type
    Method
    Description
    invoke(Object proxy, Method method, Object[] args)
    处理代理实例上的方法调用并返回结果。
    static Object
    invokeDefault(Object proxy, Method method, Object... args)
    在给定的proxy实例上调用指定的默认方法,使用给定的参数。
  • Method Details

    • 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()) {
                       // 如果是默认方法,调用它
                       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")) {
                       // 调用B::m而不是A::m
                       Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes());
                       return InvocationHandler.invokeDefault(o, bMethod, params);
                   }
               });
       
      如果代理实例实现了C,该接口从其超接口A继承了方法m的默认实现,则在代理实例上的接口方法调用"m"将被分派到调用处理程序的invoke方法,其中Method对象参数表示默认方法A::m
      
       Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
              (o, m, params) -> {
                   if (m.isDefault()) {
                       // 行为就像调用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,因为{@code A::m}不是从其代理接口C继承的方法
                       Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes());
                       return InvocationHandler.invokeDefault(o, aMethod params);
                   }
               });
       c.m(...);
       
      上述代码在旧版本的C上成功运行,并调用A::m。当在新版本的C上运行时,上述代码将因为C覆盖了相同方法的实现而导致IllegalArgumentException,并且代理实例无法访问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