Quando Java genera un'eccezione UndeclaredThrowableException?

Java Top

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO

1. Panoramica

In questo tutorial, vedremo cosa fa sì che Java generi un'istanza dell'eccezione UndeclaredThrowableException .

Innanzitutto, inizieremo con un po 'di teoria. Quindi, proveremo a comprendere meglio la natura di questa eccezione con due esempi del mondo reale.

2. UndeclaredThrowableException

Teoricamente parlando, Java lancerà un'istanza di UndeclaredThrowableException quando proviamo a lanciare un'eccezione controllata non dichiarata. Cioè, non abbiamo dichiarato l'eccezione controllata nella clausola throws ma lanciamo quell'eccezione nel corpo del metodo.

Si potrebbe obiettare che ciò è impossibile poiché il compilatore Java lo impedisce con un errore di compilazione. Ad esempio, se proviamo a compilare:

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

Il compilatore Java non riesce con il messaggio:

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

Anche se la generazione di eccezioni controllate non dichiarate potrebbe non avvenire in fase di compilazione, è comunque una possibilità in fase di esecuzione. Ad esempio, consideriamo un proxy di runtime che intercetta un metodo che non genera eccezioni:

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

Se il proxy stesso genera un'eccezione verificata, dal punto di vista del chiamante, il metodo save lancia l'eccezione selezionata. Il chiamante probabilmente non sa nulla di quel proxy e incolperà il salvataggio per questa eccezione.

In tali circostanze, Java racchiuderà l'eccezione selezionata effettiva all'interno di un'eccezione UndeclaredThrowableException e genererà invece l'eccezione UndeclaredThrowableException . Vale la pena ricordare che la stessa UndeclaredThrowableException è un'eccezione non controllata.

Ora che sappiamo abbastanza sulla teoria, vediamo alcuni esempi del mondo reale.

3. Java Dynamic Proxy

Come primo esempio, creiamo un proxy runtime per l' interfaccia java.util.List e intercettiamo le sue chiamate al metodo. Innanzitutto, dovremmo implementare l' interfaccia InvocationHandler e inserire lì la logica aggiuntiva:

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); } }

Questo proxy genera un'eccezione selezionata se il metodo proxy è size. Altrimenti, genererà un'eccezione non controllata.

Vediamo come Java gestisce entrambe le situazioni. Innanzitutto, chiameremo il metodo List.size () :

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);

Come mostrato sopra, creiamo un proxy per l' interfaccia List e chiamiamo il metodo size su di esso. Il proxy, a sua volta, intercetta la chiamata e genera un'eccezione controllata. Quindi, Java avvolge questa eccezione selezionata all'interno di un'istanza di UndeclaredThrowableException.Questo sta accadendo perché in qualche modo lanciamo un'eccezione controllata senza dichiararla nella dichiarazione del metodo.

Se chiamiamo qualsiasi altro metodo sull'interfaccia List :

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

Poiché il proxy genera un'eccezione non controllata, Java consente all'eccezione di propagarsi così com'è.

4. Aspetto primaverile

La stessa cosa accade quando lanciamo un'eccezione controllata in un aspetto Spring mentre i metodi consigliati non li dichiaravano. Cominciamo con un'annotazione:

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

Ora consiglieremo tutti i metodi annotati con questa annotazione:

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

Fondamentalmente, questo consiglio farà in modo che tutti i metodi annotati generino un'eccezione controllata, anche se non hanno dichiarato tale eccezione . Ora creiamo un servizio:

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

Se chiamiamo il metodo annotato, Java lancerà un'istanza di eccezione UndeclaredThrowableException :

@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); } }

Come mostrato sopra, Java incapsula l'eccezione effettiva come causa e genera invece l'eccezione UndeclaredThrowableException .

5. conclusione

In questo tutorial, abbiamo visto cosa fa sì che Java generi un'istanza dell'eccezione UndeclaredThrowableException .

Come al solito, tutti gli esempi sono disponibili su GitHub.

Fondo Java

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO