Bilakah Java Throw UndeclaredThrowableException?

Java Teratas

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat apa yang menyebabkan Java membuang contoh pengecualian UndeclaredThrowableException .

Pertama, kita akan mulakan dengan sedikit teori. Kemudian, kami akan berusaha untuk lebih memahami hakikat pengecualian ini dengan dua contoh dunia nyata.

2. Pengecualian yang Tidak Diisytiharkan

Secara teorinya, Java akan membuang contoh UndeclaredThrowableException ketika kita cuba membuang pengecualian yang tidak diisytiharkan. Maksudnya, kami tidak menyatakan pengecualian yang diperiksa dalam klausa lemparan tetapi kami membuang pengecualian tersebut dalam badan metode.

Seseorang mungkin berpendapat bahawa ini mustahil kerana penyusun Java mencegahnya dengan kesalahan kompilasi. Contohnya, jika kita cuba menyusun:

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

Penyusun Java gagal dengan mesej:

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

Walaupun melontarkan pengecualian yang tidak diisytiharkan mungkin tidak berlaku pada waktu kompilasi, masih ada kemungkinan pada waktu runtime. Sebagai contoh, mari kita pertimbangkan proksi runtime memintas kaedah yang tidak membuang pengecualian:

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

Sekiranya proksi itu sendiri melontarkan pengecualian yang diperiksa, dari perspektif pemanggil, kaedah simpan membuang pengecualian yang diperiksa. Pemanggil mungkin tidak mengetahui apa-apa mengenai proksi itu dan akan menyalahkan penjimatan untuk pengecualian ini.

Dalam keadaan seperti itu, Java akan membungkus pengecualian yang diperiksa sebenarnya di dalam UndeclaredThrowableException dan membuang UndeclaredThrowableException sebagai gantinya. Perlu disebutkan bahawa UndeclaredThrowableException itu sendiri adalah pengecualian yang tidak dicentang.

Sekarang kita sudah cukup mengetahui teori, mari kita lihat beberapa contoh dunia nyata.

3. Proksi Dinamik Java

Sebagai contoh pertama kami, mari buat proksi runtime untuk java.util. Daftar antara muka dan memintas panggilan kaedahnya Pertama, kita harus melaksanakan antara muka InvocationHandler dan meletakkan logik tambahan di sana:

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

Proksi ini membuang pengecualian yang diperiksa jika kaedah proksi adalah ukuran. Jika tidak, ia akan membuang pengecualian yang tidak dicentang.

Mari lihat bagaimana Java menangani kedua-dua keadaan. Pertama, kita akan memanggil kaedah 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);

Seperti yang ditunjukkan di atas, kami membuat proksi untuk antara muka Senarai dan memanggil kaedah ukuran di atasnya. Proksi, pada gilirannya, memintas panggilan dan membuang pengecualian yang diperiksa. Kemudian, Java membungkus pengecualian yang diperiksa ini di dalam contoh UndeclaredThrowableException.Ini berlaku kerana kita entah bagaimana membuang pengecualian yang diperiksa tanpa menyatakannya dalam deklarasi kaedah.

Sekiranya kita memanggil kaedah lain pada antara muka Senarai :

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

Oleh kerana proksi melontarkan pengecualian yang tidak dicentang, Java membiarkan pengecualian menyebarkan apa adanya.

4. Aspek Musim Bunga

Perkara yang sama berlaku ketika kita membuang pengecualian yang diperiksa di Spring Aspect sementara kaedah yang disarankan tidak menyatakannya. Mari mulakan dengan anotasi:

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

Sekarang kita akan menasihati semua kaedah yang diberi penjelasan dengan penjelasan ini:

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

Pada dasarnya, nasihat ini akan membuat semua kaedah yang dijelaskan untuk membuang pengecualian yang diperiksa, walaupun mereka tidak menyatakan pengecualian tersebut . Sekarang, mari buat perkhidmatan:

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

Sekiranya kita memanggil kaedah anotasi, Java akan membuang contoh pengecualian 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); } }

Seperti yang ditunjukkan di atas, Java merangkumi pengecualian yang sebenarnya sebagai penyebabnya dan sebaliknya menjadikan pengecualian UndeclaredThrowableException .

5. Kesimpulan

Dalam tutorial ini, kami melihat apa yang menyebabkan Java membuang contoh pengecualian UndeclaredThrowableException .

Seperti biasa, semua contoh boleh didapati di GitHub.

Bahagian bawah Java

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS