Kapan Java Melempar UndeclaredThrowableException?

Java Top

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

>> LIHAT KURSUSnya

1. Ikhtisar

Dalam tutorial ini, kita akan melihat apa yang menyebabkan Java memunculkan instance pengecualian UndeclaredThrowableException .

Pertama, kita akan mulai dengan sedikit teori. Kemudian, kami akan mencoba untuk lebih memahami sifat pengecualian ini dengan dua contoh dunia nyata.

2. The UndeclaredThrowableException

Secara teoritis, Java akan menampilkan instance UndeclaredThrowableException saat kami mencoba menampilkan pengecualian centang yang tidak dideklarasikan. Artinya, kami tidak mendeklarasikan pengecualian yang dicentang di klausa lemparan tetapi kami membuang pengecualian itu di badan metode.

Orang mungkin berpendapat bahwa ini tidak mungkin karena compiler Java mencegahnya dengan kesalahan kompilasi. Misalnya, jika kita mencoba mengkompilasi:

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

Kompilator Java gagal dengan pesan:

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

Meskipun membuang pengecualian yang dicentang yang tidak dideklarasikan mungkin tidak terjadi pada waktu kompilasi, hal itu masih dimungkinkan pada waktu proses. Misalnya, mari pertimbangkan proxy waktu proses yang mencegat metode yang tidak memberikan pengecualian apa pun:

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

Jika proxy itu sendiri melontarkan pengecualian yang dicentang, dari perspektif pemanggil, metode penyimpanan akan menampilkan pengecualian yang dicentang tersebut. Penelepon mungkin tidak tahu apa-apa tentang proxy itu dan akan menyalahkan penyimpanan untuk pengecualian ini.

Dalam keadaan seperti itu, Java akan menggabungkan pengecualian yang diperiksa sebenarnya di dalam UndeclaredThrowableException dan sebagai gantinya menampilkan UndeclaredThrowableException . Perlu disebutkan bahwa UndeclaredThrowableException itu sendiri merupakan pengecualian yang tidak dicentang.

Sekarang setelah kita cukup mengetahui tentang teori tersebut, mari kita lihat beberapa contoh dunia nyata.

3. Proxy Dinamis Java

Sebagai contoh pertama kita, mari kita buat proxy waktu proses untuk antarmuka java.util.List dan mencegat pemanggilan metodenya. Pertama, kita harus mengimplementasikan antarmuka InvocationHandler dan meletakkan logika ekstra 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 menampilkan pengecualian jika metode yang di-proxy-kan adalah size. Jika tidak, itu akan memunculkan pengecualian yang tidak dicentang.

Mari kita lihat bagaimana Java menangani kedua situasi tersebut. Pertama, kita akan memanggil metode 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 proxy untuk antarmuka Daftar dan memanggil metode ukuran di atasnya. Proksi, pada gilirannya, mencegat panggilan dan melontarkan pengecualian yang dicentang. Kemudian, Java membungkus pengecualian yang dicentang ini di dalam instance UndeclaredThrowableException.Ini terjadi karena kita entah bagaimana melempar pengecualian yang dicentang tanpa mendeklarasikannya dalam deklarasi metode.

Jika kita memanggil metode lain pada antarmuka Daftar :

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

Karena proxy melontarkan pengecualian yang tidak dicentang, Java memungkinkan pengecualian untuk menyebar sebagaimana adanya.

4. Aspek Musim Semi

Hal yang sama terjadi saat kami menampilkan pengecualian yang dicentang di Spring Aspect sementara metode yang disarankan tidak mendeklarasikannya. Mari kita mulai dengan anotasi:

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

Sekarang kami akan menyarankan semua metode yang dianotasi dengan anotasi 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, saran ini akan membuat semua metode beranotasi menampilkan pengecualian yang dicentang, bahkan jika metode tersebut tidak mendeklarasikan pengecualian tersebut . Sekarang, mari buat layanan:

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

Jika kita memanggil metode anotasi, Java akan memunculkan 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 merangkum pengecualian sebenarnya sebagai penyebab dan sebagai gantinya menampilkan pengecualian UndeclaredThrowableException .

5. Kesimpulan

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

Seperti biasa, semua contoh tersedia di GitHub.

Jawa bawah

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

>> LIHAT KURSUSnya