Exception Yönetimi — Hata Yukarı Çıkarken Ne Kaybolur
30 saniyede özet
Hatayı yakalamak kolay, doğru yakalamak zor. Boş bir catch hatayı sessizce yutar, sebebini eklemeden sarmak izi siler, finally içinde yanlış kapatılan bir kaynak da asıl hatanın üstünü örter.
Bir müşterinin siparişi ortadan kaybolmuş. Uygulama “tamam” demiş, log’da tek satır yok. Suçlu karmaşık bir hata değil, içi boş bir catch bloğu.
Adım adım oku
- Hata en alt katmanda, veritabanı çağrısında doğar: SQLException.
- Boş catch onu servis katmanında yutar. En üstte her şey tamam görünür, sipariş ise kaybolmuştur.
- Sebebini eklemeden yeni bir hataya sarmak, hatayı yukarı taşır ama asıl yerin izini koparır.
- Sebebiyle birlikte sarmak hem anlamlı bir hata verir hem de veritabanındaki asıl hataya giden izi korur.
-
Bayt: Müşterinin siparişi kaybolmuş! Uygulama tamam demiş, log'da tek satır yok.
-
Sen: Hata nereye gitti peki?
-
Bayt: Birisi onu yakalayıp hiçbir şey yapmadan bırakmış: içi boş bir catch.
-
Bayt: Hatayı yakaladıysan ya çöz ya da sebebiyle birlikte yukarı ilet. Paketi çöpe atmak yok!
Checked mi, unchecked mi
checked exceptionDerleyicinin yakalanmasını ya da imzada `throws` ile bildirilmesini zorunlu tuttuğu exception. `Exception`'dan türeyip `RuntimeException`'dan türemeyenler.Unchecked exception'lar (`RuntimeException` ve alt tipleri) bu zorunluluktan muaftır. Spring gibi çerçeveler checked exception'ları genelde unchecked olanlara çevirir.Sözlükte gör →, derleyicinin “bununla ilgilen” diye zorladığı hata türüdür: ya yakalarsın ya da throws yazıp işi çağırana bırakırsın. RuntimeException ve ondan türeyenler bu zorunluluktan muaftır.
| Tür | Örnek | Ne zaman |
|---|---|---|
| Checked | IOException, SQLException | Çağıranın toparlanabileceği dış dünya hatası |
| Unchecked | IllegalArgumentException, NullPointerException | Programlama hatası — yakalama, düzelt |
| Error | OutOfMemoryError, StackOverflowError | JVM’in kendi sorunu — yakalama |
Çağıran bu hatayla anlamlı bir şey yapamıyorsa, onu checked yapma. Yoksa her çağıranı boş bir catch yazmaya zorlarsın. Spring de bu yüzden SQLException’ı unchecked DataAccessException’a çevirir.
Kafam karıştı, daha basit anlat
Checked hata, derleyicinin “bununla ilgilen” dediği hatadır. Dosya bulunamadı gibi, çağıranın bir şey yapabileceği durumlar için. Kodundaki bir yanlış ise unchecked olur: onu yakalamazsın, düzeltirsin.
Checked ile unchecked exception arasındaki fark nedir?
Her birini doğru gruba yerleştir.
Satır satır: iki hata, tek catch
Peki bir iş patlarken, arkasından kapatmaya çalıştığın bağlantı da patlarsa? Ağ koptuğunda tam olarak bu yaşanır: iki hata üst üste.
INSERT duplicate key ile patladı, ardından connection'ın close()'u da patladı. catch bloğuna hangi hata gelir? Cevabı göster
Asıl hata: duplicate key. try-with-resources ikinci hatayı fırlatmaz, asıl hataya ekler. İkisi de log’a düşer.
close() da patlarsa
try (var conn = dataSource.getConnection()) { insert(conn, order);} catch (SQLException e) { log.error("kayıt başarısız", e); var hidden = e.getSuppressed();}Debug
repository Bağlantı alınır. try-with-resources, blok nasıl biterse bitsin close() çağıracağını garanti eder.
- conn
- = açık
Sol/sağ ok tuşlarıyla da gezebilirsin.
İkinci hataya suppressed exceptiontry-with-resources'ta kaynak kapatılırken atılan ve asıl hatanın yerine geçmek yerine ona eklenen ikinci exception. `getSuppressed()` ile okunur.Sözlükte gör → denir: asıl hatanın yanına not olarak iliştirilir. Elle yazılmış finally bunu yapmaz: oradan fırlayan hata asıl hatanın yerini alır ve asıl sebep iz bırakmadan kaybolur.
Kafam karıştı, daha basit anlat
Asıl hata ön sayfadır. Kapatırken çıkan ikinci hata, arkasına iliştirilmiş küçük bir nottur. try-with-resources ikisini de saklar, elle yazılan finally ise ön sayfayı kaybedebilir.
Program ne yazdırır?
Kendin gör
Exception akışı — hata yukarı çıkarken ne kaybolur?
Tohum 658401// OrderRepository.inserttry (var conn = dataSource.getConnection()) { conn.prepareStatement(INSERT).executeUpdate(); // SQLException} // close() burada, her durumda // OrderService.placerepository.insert(order); // yakalamıyor- OrderRepository.insertbekliyor
- Connectionbekliyor
- OrderService.placebekliyor
- İstemcibekliyor
Şu an ne oldu?
POST /orders geldi
Aynı sipariş numarası ikinci kez yazılmaya çalışılacak. Hatayı katman katman izle.
Görevler0/3
İstemci 200 alsın, sipariş kaydedilmesinaçık
İpucu
Service'in hatayı yakalayıp hiçbir şey yapmadığı seçeneği dene.
İstemci 500 alsın ama log'da SQLException hiç görünmesinaçık
İpucu
İki yolu var: cause vermeden sarmak ya da finally içindeki close() hatasının asıl hatayı ezmesi.
close() da patlarken iki hata da log'a düşsünaçık
İpucu
close() patlasın. Hangi kapatma biçimi ikinci hatayı atmak yerine asıl hataya ekler?
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. HTTP 500, log’da SQLException, bağlantı havuza döndü. Hedef bu.
- Service’i “yutuyor” yap. İstemci 200 aldı, log boş, sipariş yok.
- “Sarıyor, cause vermiyor” seç. 500 var ama log’da yalnızca
OrderFailedException. - close() patlasın, kapatmayı
finallyyap. Log bağlantıdan bahsediyor, duplicate key izi yok. - “INSERT’ten sonra close()” seç. Hata görünüyor ama bağlantı sızdı.
try-with-resources gövdesi SQLException fırlattı, ardından kaynağın close() metodu da bir exception fırlattı. catch bloğuna hangisi gelir?
Sararken sebebini de ekle
Teknik bir hatayı (“veritabanı cevap vermedi”) iş diline (“sipariş alınamadı”) çevirmek doğru bir fikir. Ekrana cevap veren katmanın SQLException bilmesi gerekmez.
try { repository.insert(order);} catch (SQLException e) { throw new OrderFailedException("sipariş kaydedilemedi", e); // e = cause}Çevirirken asıl sebebi (cause) de yanına koy. exception chainingBir exception'ı başka birine sararken asıl hatayı `cause` olarak geçirmek. Stack trace'teki "Caused by" satırları buradan gelir.Sözlükte gör → sayesinde log’da “Caused by: SQLException” satırı kalır. Sebebi eklemezsen iz, tam da hatanın çıktığı yerde kopar.
- Yakaladığın yerde bir şey yap. Toparla ya da anlamını değiştir. İkisi de değilse yakalama.
- Ya logla ya fırlat, ikisini birden değil. Her katman loglarsa bir hata beş alarm olur.
- Kenarda tek yerde yakala. Spring’de bu
@RestControllerAdvice’tır.
Kafam karıştı, daha basit anlat
Hatayı iş diline çevirebilirsin, ama asıl sebebi de yanına koy. Böylece log’u okuyan kişi, en alttaki gerçek sorunu hâlâ görebilir.
Üretimde "sipariş kaydedilemedi" hatası görülüyor ama log'dan nedeni anlaşılamıyor. Hangi satırlar sorunun kaynağı?
Tuzaklar
finally içinde return. Yolda olan hatayı sessizce yok eder ve metot hiçbir şey olmamış gibi “başarılı” döner.
InterruptedException'ı yutmak· istersen atla
Bu hatayı yakalıyorsan Thread.currentThread().interrupt() ile bayrağı geri koy. Yoksa thread’i durdurmak isteyen kod bu isteği bir daha göremez.
Checked exception ve @Transactional. Spring varsayılan olarak yalnızca unchecked exception’da geri alır. Checked bir exception metottan çıkarsa transaction yine de kaydedilir; bunu istemiyorsan rollbackFor yaz.
Hatayı normal akış için kullanmak. Exception üretmek ucuz değildir ve kodun niyetini gizler. “Bulunamadı” gibi zaten beklenen durumlar için Optional dön.
`try { throw new IllegalStateException(); } finally { return 42; }` olan bir metot çağrıldığında ne olur?
Aşağıdaki örnek bir bankanın kredi taksidi tahsilatından: ana bankacılık sisteminin hatası sebebiyle birlikte sarılıyor, kaynaklar her yolda kapanıyor ve sınırda tek bir yerde anlamlı bir cevaba çevriliyor.
Derinleş · Kredi taksidi tahsilatı: katmanlar boyunca exception 5 dosya · ~107 satır · ilk okumada atlayabilirsin
Kendini sına
İçi boş bir catch bloğu hatayı güvenle ortadan kaldırır.
Bir worker döngüsünde `Thread.sleep()` çağrısının InterruptedException'ını yakalaman gerekiyor ve metot imzasına throws ekleyemiyorsun. Ne yaparsın?
Aklında kalacak üç şey
- 1 Checked exception, çağıranın bir şey yapabileceği dış dünya hataları içindir: dosya yok, ağ koptu. Programcı hatası unchecked'tır.
- 2 try-with-resources, kapatırken çıkan hatayı asıl hatanın yerine koymaz, ona not olarak ekler. Elle yazılmış finally bunu yapmaz.
- 3 Bir exception'ı başka birine sararken sebebini (cause) eklemezsen, hatanın asıl çıktığı yerin izi kaybolur.
5 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.