1. Overview
When working with Java Reflection API, it is common to encounter java.lang.reflect.InvocationTargetException.
In this tutorial, we’ll take a look at how to handle it with a simple example.
2. Cause of InvocationTargetException
It mainly occurs when we work with the reflection layer and try to invoke a method or constructor that throws an underlying exception itself.
The reflection layer wraps the actual exception thrown by the method with the InvocationTargetException.
Let’s try to understand it with an example.
We’ll write a class with a method that intentionally throws an exception:
public class InvocationTargetExample {
public int divideByZeroExample() {
return 1 / 0;
}
}
Let’s invoke the above method using reflection in a simple JUnit 5 Test:
InvocationTargetExample targetExample = new InvocationTargetExample();
Method method = InvocationTargetExample.class.getMethod("divideByZeroExample");
Exception exception = assertThrows(InvocationTargetException.class, () -> method.invoke(targetExample));
In the above code, we have asserted the InvocationTargetException, which is thrown while invoking the method. To investigate an exception more easily, we usually check its stack trace. So, let’s print the stack trace of this exception:
LOG.error("InvocationTargetException Thrown:", exception);
Then, we can see the error log in the console:
18:08:28.662 [main] ERROR ...InvocationTargetUnitTest -- InvocationTargetException Thrown
java.lang.reflect.InvocationTargetException: null
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at com.baeldung.reflection.exception.invocationtarget.InvocationTargetUnitTest.lambda$whenCallingMethodThrowsException_thenAssertCauseOfInvocationTargetException$0(InvocationTargetUnitTest.java:23)
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:53)
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:35)
at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3128)
at com.baeldung.reflection.exception.invocationtarget.InvocationTargetUnitTest.whenCallingMethodThrowsException_thenAssertCauseOfInvocationTargetException(InvocationTargetUnitTest.java:23)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
...
Caused by: java.lang.ArithmeticException: / by zero
at com.baeldung.reflection.exception.invocationtarget.InvocationTargetExample.divideByZeroExample(InvocationTargetExample.java:5)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
... 74 common frames omitted
It’s important to note that the actual exception — ArithmeticException in this case — gets wrapped into an InvocationTargetException. So, some may ask, why doesn’t reflection throw the actual exception in the first place?
This is because the purpose of InvocationTargetException is to provide a way to distinguish between exceptions thrown during the reflective call itself and exceptions thrown by the method being invoked.
3. How to Handle InvocationTargetException?
As InvocationTargetException wraps the actual cause, when we investigate the stack trace, we must search for the actual underlying exception. This can be inconvenient. Moreover, we might need to access the underlying exception programmatically to handle it appropriately in our code.
To achieve that, we can use Throwable.getCause() to obtain the actual cause of the InvocationTargetException.
Next, let’s use the same example above to see how this is usually done in our applications:
try {
InvocationTargetExample targetExample = new InvocationTargetExample();
Method method = InvocationTargetExample.class.getMethod("divideByZeroExample");
method.invoke(targetExample);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof ArithmeticException) {
// ... handle ArithmeticException
LOG.error("InvocationTargetException caused by ArithmeticException:", cause);
} else {
// ... handle other exceptions
LOG.error("The underlying exception of InvocationTargetException:", cause);
}
} catch (NoSuchMethodException | IllegalAccessException e) {
// for simplicity, rethrow reflection exceptions as RuntimeException
throw new RuntimeException(e);
}
In this example, in the InvocationTargetException catch block, getCause() gives us the underlying exception.
Once we get the underlying exception, we can handle it or wrap it in some custom exception. This allows us to handle exceptions in a more specific and controlled manner.
In the example, we want to log the cause‘s stack trace if it’s ArithmeticException. So, we’ll see the underlying exception’s stack trace if we run the code:
18:39:10.946 [main] ERROR ...InvocationTargetUnitTest -- InvocationTargetException caused By ArithmeticException:
java.lang.ArithmeticException: / by zero
at com.baeldung.reflection.exception.invocationtarget.InvocationTargetExample.divideByZeroExample(InvocationTargetExample.java:5)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
...
As we can see, the output clearly shows us the actual exception type, the message, and which line of code in which class led to it.
4. Conclusion
InvocationTargetException is a common exception encountered when working with reflection in Java.
In this quick article, we’ve discussed what an InvocationTargetException is. We’ve also explored how to determine its underlying cause and how to handle such a scenario through a simple example.
Knowing how to manage InvocationTargetException effectively is an essential skill for debugging or handling errors in our applications.
As usual, the code used in this article is available over on GitHub.