在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”關鍵字,這並不表示是業務代碼中拋出了空指針異常。
如下示例代碼:
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
分析完畢!