Wann löst Java UndeclaredThrowableException aus?

Java Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

In diesem Tutorial werden wir sehen, warum Java eine Instanz der UndeclaredThrowableException- Ausnahme auslöst .

Zuerst beginnen wir mit ein bisschen Theorie. Dann werden wir versuchen, die Natur dieser Ausnahme anhand von zwei Beispielen aus der Praxis besser zu verstehen.

2. Die UndeclaredThrowableException

Theoretisch löst Java eine Instanz von UndeclaredThrowableException aus, wenn wir versuchen, eine nicht deklarierte geprüfte Ausnahme auszulösen. Das heißt, wir haben die geprüfte Ausnahme nicht erklären in der wirft Klausel aber wir werfen diese Ausnahme in der Methode Körper.

Man könnte argumentieren, dass dies unmöglich ist, da der Java-Compiler dies mit einem Kompilierungsfehler verhindert. Wenn wir zum Beispiel versuchen zu kompilieren:

public void undeclared() { throw new IOException(); }

Der Java-Compiler schlägt mit der folgenden Meldung fehl:

java: unreported exception java.io.IOException; must be caught or declared to be thrown

Auch wenn das Auslösen nicht deklarierter geprüfter Ausnahmen zur Kompilierungszeit möglicherweise nicht erfolgt, ist dies zur Laufzeit dennoch möglich. Betrachten wir beispielsweise einen Laufzeit-Proxy, der eine Methode abfängt, die keine Ausnahmen auslöst:

public void save(Object data) { // omitted }

Wenn der Proxy selbst aus Sicht des Aufrufers eine aktivierte Ausnahme auslöst, löst die Speichermethode diese aktivierte Ausnahme aus. Der Anrufer weiß wahrscheinlich nichts über diesen Proxy und wird die Speicherung für diese Ausnahme verantwortlich machen.

Unter solchen Umständen wird Java die tatsächlich überprüfte Ausnahme in eine UndeclaredThrowableException einschließen und stattdessen die UndeclaredThrowableException auslösen . Es ist erwähnenswert, dass die UndeclaredThrowableException selbst eine ungeprüfte Ausnahme ist.

Nachdem wir genug über die Theorie wissen, sehen wir uns einige Beispiele aus der Praxis an.

3. Java Dynamic Proxy

Als erstes Beispiel erstellen wir einen Laufzeit-Proxy für die Schnittstelle java.util.List und fangen dessen Methodenaufrufe ab. Zuerst sollten wir die InvocationHandler- Schnittstelle implementieren und die zusätzliche Logik dort platzieren:

public class ExceptionalInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("size".equals(method.getName())) { throw new SomeCheckedException("Always fails"); } throw new RuntimeException(); } } public class SomeCheckedException extends Exception { public SomeCheckedException(String message) { super(message); } }

Dieser Proxy löst eine aktivierte Ausnahme aus, wenn die Proxy-Methode die Größe hat. Andernfalls wird eine ungeprüfte Ausnahme ausgelöst.

Mal sehen, wie Java mit beiden Situationen umgeht. Zuerst rufen wir die List.size () -Methode auf:

ClassLoader classLoader = getClass().getClassLoader(); InvocationHandler invocationHandler = new ExceptionalInvocationHandler(); List proxy = (List) Proxy.newProxyInstance(classLoader, new Class[] { List.class }, invocationHandler); assertThatThrownBy(proxy::size) .isInstanceOf(UndeclaredThrowableException.class) .hasCauseInstanceOf(SomeCheckedException.class);

Wie oben gezeigt, schaffen wir einen Proxy für die Liste Schnittstelle und die rufen Größe Methode auf sich. Der Proxy fängt wiederum den Anruf ab und löst eine aktivierte Ausnahme aus. Anschließend schließt Java diese überprüfte Ausnahme in eine Instanz von UndeclaredThrowableException ein.Dies geschieht, weil wir irgendwie eine aktivierte Ausnahme auslösen, ohne sie in der Methodendeklaration zu deklarieren.

Wenn wir eine andere Methode auf der List- Schnittstelle aufrufen :

assertThatThrownBy(proxy::isEmpty).isInstanceOf(RuntimeException.class);

Da der Proxy eine ungeprüfte Ausnahme auslöst, lässt Java die Ausnahme unverändert weitergeben.

4. Frühlingsaspekt

Das gleiche passiert, wenn wir eine aktivierte Ausnahme in einem Spring-Aspekt auslösen, während die empfohlenen Methoden sie nicht deklariert haben. Beginnen wir mit einer Anmerkung:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ThrowUndeclared {}

Jetzt werden wir alle Methoden empfehlen, die mit dieser Anmerkung versehen sind:

@Aspect @Component public class UndeclaredAspect { @Around("@annotation(undeclared)") public Object advise(ProceedingJoinPoint pjp, ThrowUndeclared undeclared) throws Throwable { throw new SomeCheckedException("AOP Checked Exception"); } }

Grundsätzlich werden mit diesem Rat alle mit Anmerkungen versehenen Methoden zum Auslösen einer aktivierten Ausnahme verwendet, auch wenn sie eine solche Ausnahme nicht deklariert haben . Jetzt erstellen wir einen Service:

@Service public class UndeclaredService { @ThrowUndeclared public void doSomething() {} }

Wenn wir die mit Anmerkungen versehene Methode aufrufen, löst Java eine Instanz der UndeclaredThrowableException- Ausnahme aus:

@RunWith(SpringRunner.class) @SpringBootTest(classes = UndeclaredApplication.class) public class UndeclaredThrowableExceptionIntegrationTest { @Autowired private UndeclaredService service; @Test public void givenAnAspect_whenCallingAdvisedMethod_thenShouldWrapTheException() { assertThatThrownBy(service::doSomething) .isInstanceOf(UndeclaredThrowableException.class) .hasCauseInstanceOf(SomeCheckedException.class); } }

Wie oben gezeigt, kapselt Java die eigentliche Ausnahme als Ursache und löst stattdessen die Ausnahme UndeclaredThrowableException aus .

5. Schlussfolgerung

In diesem Tutorial haben wir gesehen, warum Java eine Instanz der UndeclaredThrowableException- Ausnahme auslöst .

Wie üblich sind alle Beispiele auf GitHub verfügbar.

Java unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs