UndeclaredThrowableException原因

UndeclaredThrowableException 原因

RPC 请求的时候抛出该异常,异常点是 RPC 调用的地方抛出的该异常,除此之外没有其他的异常信息。仔细排查,实际的异常应该是网络IO的TIMEOUT导致的。 那么,就有2个问题需要探讨:

  • 为啥抛出的是UndeclaredThrowableException,而不是 TimeOut 的 Exception
  • 如何抛出 TimeOut 异常

为啥抛出的是UndeclaredThrowableException

先说结论,之所以抛出这个异常,是因为在RPC调用的实现中,使用了 JDK 动态代理的原因。在 JDK Proxy 的调用中,如果实际运行时(InvocationHandler#invoke) 抛出了某个**受检异常(checked exception)**,但该受检异常并未在被代理对象接口定义中进行声明,那么这个异常就会被 JVM包装成 UndeclaredThrowableException 进行抛出。

这里面有个受检异常的概念,简单解释一下,Java 中所有异常,都继承自 java.lang.Throwable 类。

Throwable有两个直接子类,Error 类和 Exception 类。

Error 类型用于指示合理的应用程序不应该试图捕获的严重问题, 是一种 UncheckedExcepiton

Exception 可分为 RuntimeExceptionChecked Exception 两种, 而 RuntimeExceptionJVM 正常运行期间抛出的异常的超类时抛出,也是UncheckedExcepiton

  • 受检异常

    1
    2
    3
    4
    5
    6
    7
    8
    java.lang.ClassNotFoundException
    java.lang.CloneNotSupportedException
    java.lang.IllegalAccessException
    java.lang.InterruptedException
    java.lang.NoSuchFieldException
    java.lang.NoSuchMetodException
    java.io.IOException
    ...
  • 非受检异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    java.lang.ArithmeticException
    java.lang.ArrayStoreExcetpion
    java.lang.ClassCastException
    java.lang.EnumConstantNotPresentException
    java.lang.IllegalArgumentException
    java.lang.IllegalThreadStateException
    java.lang.NumberFormatException
    java.lang.IllegalMonitorStateException
    java.lang.IllegalStateException
    java.lang.IndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException
    java.lang.StringIndexOutOfBoundsException
    java.lang.NegativeArraySizeException’
    java.lang.NullPointerException
    java.lang.SecurityException
    java.lang.TypeNotPresentException
    java.lang.UnsupprotedOperationException
    ...

如何抛出 TimeOut 异常

  • 可以通过UndeclaredThrowableException#getUndeclaredThrowable拿到被包装的受检异常;JDK1.4以后,通过*Throwable#getCause也可以拿到被包装的受检异常,而且这是被建议的方式,因为前者已经过时了

  • 实际上,method.invoke的方法申明如下:

    1
    public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

    Method.invoke 的方法本身只申明了3种异常,正常调用部分的异常会被 InvocationTargetException 包裹,实际上是 2 层包裹。 另外值得注意的是 InvocationTargetException 本身是受检异常, 既可以包裹受检异常,也可以包裹非受检异常。而 UndeclaredThrowableException 本身是非受检异常, 只可以包裹受检异常

  • 参看 Spring处理方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // org.springframework.aop.support.AopUtils

    public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable {

    // Use reflection to invoke the method.
    try {
    ReflectionUtils.makeAccessible(method);
    return method.invoke(target, args);
    }
    catch (InvocationTargetException ex) {
    // Invoked method threw a checked exception.
    // We must rethrow it. The client won't see the interceptor.
    // 重点在此处:抛出被包装的原始异常
    throw ex.getTargetException();
    }
    catch (IllegalArgumentException ex) {
    throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
    method + "] on target [" + target + "]", ex);
    }
    catch (IllegalAccessException ex) {
    throw new AopInvocationException("Could not access method [" + method + "]", ex);
    }
    }