在Java中通过反射调用方法时,常见的一个异常是:java.lang.reflect.InvocationTargetException
,将异常信息打印到日志文件中时通常会有如下一句信息:java.lang.reflect.InvocationTargetException: null
,由于在异常信息中存在"null",一开始就会非常敏感,会误以为是空指针异常。
其实不然,从java.lang.reflect.Method.invoke()
方法注释描述中可以知道,当抛出InvocationTargetException
异常时表明是在执行底层方法时异常。这里的“底层”并不是指JDK的底层实现,而是相对于反射调用的入口而言,通常是业务代码的实现方法。
实际上,当出现InvocationTargetException
异常时通常会在异常堆栈中同时存在一个提示:Caused by: xxx
,只要根据这个提示就能很快定位到具体问题。
最后再来解释日志信息中为什么会出现一个关键字“null”,这很容易让人误以为是业务代码出现了空指针异常!
这是因为在通过日志框架打印异常信息时,会将Throwable.detailMessage
属性打印出来,由于在反射调用时InvocationTargetException
异常是Java本地方法抛出的,此时该异常对象的detailMessage
属性为null,因此在打印出来的日志信息中就看到了“null”关键字,这并不表示是业务代码中抛出了空指针异常。
如下示例代码:
代码语言:javascript复制public class ReflectionTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionTest.class);
public static void main(String[] args) {
try {
ReflectionTest test = new ReflectionTest();
Method method = test.getClass().getMethod("methodInvokeTest");
method.invoke(test);
} catch (Exception e) {
LOGGER.error("{}", "test", e);
}
}
public void methodInvokeTest() {
if (1 == 1) {
throw new RuntimeException("在业务方法中抛出异常");
}
}
}
在DEBUG时可以看到InvocationTargetException
对象的detailMessage
属性为空。
在打印的日志信息中同样存在InvocationTargetException: null
(其实在业务代码中抛出的并非空指针异常)。
2024-05-06 17:46:22,228 ERROR [main] o.e.j.ReflectionTest [ReflectionTest.java:20] test
java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_261]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_261]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_261]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_261]
at org.example.java.ReflectionTest.main(ReflectionTest.java:18) ~[classes/:na]
Caused by: java.lang.RuntimeException: 在业务方法中抛出异常
at org.example.java.ReflectionTest.methodInvokeTest(ReflectionTest.java:26) ~[classes/:na]
... 5 common frames omitted
分析完毕!