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 可分为 RuntimeException 和 Checked Exception 两种, 而 RuntimeException是 JVM 正常运行期间抛出的异常的超类时抛出,也是UncheckedExcepiton。
受检异常
1
2
3
4
5
6
7
8java.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
18java.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);
}
}