---
title: 'Denetleyiciler ve Yönlendirme'
source: 'https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/denetleyiciler-ve-yonlendirme'
course: 'Kurumsal Uygulama Çerçevesi'
language: tr
updated: '2026-08-23T14:25:05+00:00'
license: 'CC BY-SA 4.0'
---

# Denetleyiciler ve Yönlendirme

Dört uç noktanın dördü de kaynakta hiçbir çağrı olmadan yalnız bir açıklamayla bildiriliyor. Eşleşmeyen bir yol istendiğinde nesne kabı hata vermiyor, sabit bir varsayılan yanıt döndürüyor; aynı yolu bildiren iki bileşen tarandığında ise kazanan tarama sırasına bağlı kalıyor.

Önceki ders bir isteğin kaptan geçerken hangi halkalardan geçtiğini ölçtü, ama halkaların
sonunda isteğin hangi işi karşıladığı hiç belirlenmedi. Bu ders o boşluğu kapatıyor: bir uç
noktanın kaynakta hangi çağrıyla değil hangi açıklamayla bildirildiğini, ve eşleşmeyen bir
isteğe kabın ne yanıt verdiğini ölçüyor. Kaynak tasarımı, bir yolun hangi kaynağı ve hangi
eylemi temsil ettiği **M16/K02 API Tasarımı** kursunda kavram olarak kuruldu ve burada
tekrarlanmıyor; bu dersin sorduğu soru ondan ayrı — bir yolun kaynakta **nasıl var olduğu**.

Denetleyici burada da modellenir: gerçek bir HTTP yöntemi ayrıştırılmaz, gerçek bir istek
gövdesi çözülmez. Bir "yol" yalnız bir dizgedir (`"/urun/liste"` gibi), ve bir uç nokta bu
dizgeyi bir Java yöntemine bağlayan bir eşleştirmedir. Önceki dersteki halkalar isteği
değiştiriyordu, yönlendirmiyordu; bu dersin denetleyicileri ise isteği hiç değiştirmeden
doğru yönteme **yönlendiriyor** — kursun terim kararında `routing` karşılığı budur.

Bir **denetleyici** burada sıradan bir Java sınıfıdır; onu denetleyici yapan tek şey,
yöntemlerinden bazılarının üzerinde `@UcNokta` açıklamasının bulunmasıdır. Sınıfın kendisi
hiçbir arayüz uygulamıyor, hiçbir üst sınıftan türemiyor — kabın onu "denetleyici" olarak
tanıması, sınıfın tipinden değil, `tara` çağrısına elle verilmesinden geliyor. Bu, kursun
web katmanındaki ikinci modelleme kararı: gerçek çerçevelerde sınıf taraması genellikle
sınıf yolunun tamamını dolaşır, burada ise `tara`'ya hangi nesnelerin denetleyici olarak
değerlendirileceği açıkça bir dizi olarak veriliyor. Modelleme farkı sonucu değiştirmiyor —
her iki durumda da kaynakta denetleyici sınıfını **çağıran** bir satır yok, yalnız üzerinde
duran bir açıklama var.

## Dört Yolun Dördü de Açıklamadan Doğuyor

- **WV6.** `@UcNokta` yalnız bir yöntemin üzerine konur ve tek bir alan taşır: `yol`. Bu
  açıklamayı okuyan taraf, aşağıdaki `tara` yöntemidir.
- **WV7.** Yönlendirme tablosu bir `Map<String, Method>` olarak tutulur; anahtar yol
  dizgesi, değer yansımayla çağrılacak yöntemdir. Gerçek bir istek gövdesi ayrıştırma ya
  da içerik türü pazarlığı burada yoktur.

İki denetleyici sınıfı, dördü de `@UcNokta` taşıyan dört yöntem tanımlıyor. Hiçbir yerde
`kap.ekle("/urun/liste", ...)` biçiminde bir çağrı yok — önceki dersin çağrı ile eklenen
halkalarından farklı olarak, burada yönlendirme tablosunun tamamı `tara` çağrısının sınıfların
üzerindeki açıklamaları okumasıyla kuruluyor. Bu, önceki dersin karışık kaynak dağılımından
(iki varsayılan, iki açıklama, iki çağrı) ayrılan bir uç: dört uç noktanın **dördü de**
açıklamadan doğuyor, ne bir varsayılan uç nokta var ne de elle eklenen bir çağrı.

```java
// Kap.java — uc noktalar aciklamadan doguyor, cakisma kayit sirasina bagli
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface UcNokta { String yol(); }

    final Map<String, Method> yollar = new LinkedHashMap<>();
    final Map<String, Object> sahibi = new LinkedHashMap<>();
    final List<String> kayitSirasi = new ArrayList<>();

    void tara(Object... denetleyiciler) {
        for (Object d : denetleyiciler) {
            for (Method m : d.getClass().getDeclaredMethods()) {
                UcNokta u = m.getAnnotation(UcNokta.class);
                if (u == null) continue;
                yollar.put(u.yol(), m);
                sahibi.put(u.yol(), d);
                kayitSirasi.add(u.yol() + " <- " + d.getClass().getSimpleName());
            }
        }
    }

    String istek(String yol) {
        Method m = yollar.get(yol);
        if (m == null) return "varsayilan-yanit: sayfa hazir degil";
        try {
            return (String) m.invoke(sahibi.get(yol));
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        UrunDenetleyici urun = new UrunDenetleyici();
        StokDenetleyici stok = new StokDenetleyici();

        Kap kap = new Kap();
        kap.tara(urun, stok);

        String[] dortYol = {"/urun/liste", "/urun/ekle", "/stok/durum", "/stok/uyari"};
        System.out.println("-- dort yolun tamami aciklamadan dogdu --");
        int aciklamadan = 0;
        for (String yol : dortYol) {
            String yanit = kap.istek(yol);
            System.out.printf("%-16s%s%n", yol, yanit);
            if (kap.yollar.containsKey(yol)) aciklamadan++;
        }
        System.out.println("aciklamadan dogan yol sayisi: " + aciklamadan + "/4");

        System.out.println("-- eslesmeyen yol --");
        System.out.println("/urun/sil       " + kap.istek("/urun/sil"));
        System.out.println("kusur turu: sessiz (istisna yok, sabit bir varsayilan yanit dondu)");

        System.out.println("-- cakisan uc nokta: iki bilesen ayni yolu bildiriyor --");
        YedekDenetleyici yedek = new YedekDenetleyici();

        Kap kapA = new Kap();
        kapA.tara(urun, yedek);
        System.out.println("tarama sirasi urun-yedek, /urun/liste yaniti: " + kapA.istek("/urun/liste"));

        Kap kapB = new Kap();
        kapB.tara(yedek, urun);
        System.out.println("tarama sirasi yedek-urun, /urun/liste yaniti: " + kapB.istek("/urun/liste"));

        System.out.println("iki tarama sirasi ayni yaniti mi veriyor: "
                + kapA.istek("/urun/liste").equals(kapB.istek("/urun/liste")));

        System.out.println("-- elle cagrilan denetleyici: kap kaydindan bagimsiz --");
        System.out.println("urun.liste() dogrudan cagrildi: " + urun.liste());
        System.out.println("kapA uzerinden /urun/liste (son kayit yedek): " + kapA.istek("/urun/liste"));
        System.out.println("elle cagri ile kap yaniti ayni mi: " + urun.liste().equals(kapA.istek("/urun/liste")));
    }
}

class UrunDenetleyici {
    @Kap.UcNokta(yol = "/urun/liste")
    public String liste() { return "3 urun"; }

    @Kap.UcNokta(yol = "/urun/ekle")
    public String ekle() { return "urun eklendi"; }
}

class StokDenetleyici {
    @Kap.UcNokta(yol = "/stok/durum")
    public String durum() { return "stok: 42"; }

    @Kap.UcNokta(yol = "/stok/uyari")
    public String uyari() { return "uyari yok"; }
}

class YedekDenetleyici {
    @Kap.UcNokta(yol = "/urun/liste")
    public String liste() { return "yedek liste"; }
}
```

```
-- dort yolun tamami aciklamadan dogdu --
/urun/liste     3 urun
/urun/ekle      urun eklendi
/stok/durum     stok: 42
/stok/uyari     uyari yok
aciklamadan dogan yol sayisi: 4/4
-- eslesmeyen yol --
/urun/sil       varsayilan-yanit: sayfa hazir degil
kusur turu: sessiz (istisna yok, sabit bir varsayilan yanit dondu)
-- cakisan uc nokta: iki bilesen ayni yolu bildiriyor --
tarama sirasi urun-yedek, /urun/liste yaniti: yedek liste
tarama sirasi yedek-urun, /urun/liste yaniti: 3 urun
iki tarama sirasi ayni yaniti mi veriyor: false
-- elle cagrilan denetleyici: kap kaydindan bagimsiz --
urun.liste() dogrudan cagrildi: 3 urun
kapA uzerinden /urun/liste (son kayit yedek): yedek liste
elle cagri ile kap yaniti ayni mi: false
```

Dört uç noktanın dördü de `aciklamadan dogan yol sayisi: 4/4` satırında doğrulanıyor. Kaynağı
okuyan biri `UrunDenetleyici` ve `StokDenetleyici` sınıflarına bakıp dört `@UcNokta`
açıklaması görür, ama bu dört açıklamanın gerçekten bir yönlendirme tablosuna dönüştüğünü
kaynaktan **çıkaramaz** — dönüşümü yapan `tara` yöntemi çağıran kodun hiç görmediği bir yerde
duruyor. Bu, önceki dersle aynı okuma sorununu tekrar üretiyor: açıklama kaynakta bir işaret
bırakıyor, ama işaretin gerçekten bir davranışa dönüştüğü yalnız kabın kendi kodunu okuyarak
anlaşılıyor.

Bu noktada iki denetleyici sınıfının kendi başına hiçbir işlevi yok — `UrunDenetleyici` ile
`StokDenetleyici`, kaba hiç tanıtılmadan da derlenir, örneklenebilir, yöntemleri çağrılabilir.
Bir sınıfı "denetleyici" yapan şey onun kendi kodu değil, kabın onu tanıması. Bu, önceki
derste `@Halka` açıklamasında da görülen kalıbın burada tekrarı: açıklama sınıfın üzerinde
durduğu sürece bir istektir, isteği yerine getirecek bir okuyucu yoksa sınıf sıradan bir Java
sınıfından farksız kalır. Fark şu: bu derste isteği yerine getiren okuyucu,
sınıfı bir bileşen olarak kurmuyor, yalnız yöntemlerinden birini bir yol dizgesine bağlıyor.
Aynı açıklama düzeneği, konuya göre tamamen farklı bir sonuç üretebiliyor — kap ne
yapacağına, açıklamanın taşıdığı bilgiye ve kendi kuralına bakarak karar veriyor.

## Eşleşmeyen Yol: Sessiz Bir Varsayılan

- **WV8.** `istek` yöntemi eşleşme bulamadığında sabit bir dizge döndürüyor, hiçbir istisna
  fırlatmıyor. Bu sabit dizge normal bir yanıtla aynı tipte (`String`) olduğu için çağıran
  taraf onu ayırt etmek zorunda kalmadan bir "yanıt" alıyor.

`/urun/sil` hiçbir denetleyicide tanımlı değil, ve `kap.istek("/urun/sil")` çağrısı bir
istisna fırlatmak yerine `"varsayilan-yanit: sayfa hazir degil"` döndürüyor. Bu, kursun dört
kusur sınıfından **sessiz** olanının bir örneği: program çöküyor demiyor, bir hata mesajı
basmıyor, yalnız beklenmeyen bir yanıtla devam ediyor. Bir geliştirici `/urun/sill` gibi bir
yazım hatası yaparsa, kod hiçbir yerde patlamaz — yanıt gelir, biçimi doğrudur, ama içeriği
hiçbir zaman gerçek bir uç noktanın ürettiği içerik değildir. Kusuru yakalayan tek şey,
yanıtın **içeriğini** bilerek karşılaştırmaktır; yanıtın **var olması** hiçbir şey
kanıtlamıyor.

Bu davranış bir tasarım kararı, bir eksiklik değil — kap eşleşmeyen her yol için aynı sabit
yanıtı üretiyor ve bu tutarlı. Ama tutarlılık ile doğruluk aynı şey değil: eşleşmeyen bir yol
her zaman aynı yanıtı üretmesi, o yanıtın çağıranın beklediği şey olduğu anlamına gelmiyor.
Önceki derste kap eksik bir tip istendiğinde `IllegalStateException` fırlatıyordu — orada kap
hiç bilmediği bir sınıfı kurmaya çalışıyordu ve bu gerçekten yürütülemez bir durumdu. Burada
ise kap bir yol bulamadığında yürütülebilir bir durumdayken sessiz kalmayı seçiyor, çünkü
"bu yol yok" bilgisini bir istisna yerine bir yanıt gövdesi olarak taşımak da kabın verdiği bir
karar.

Bu kararın alternatifi kaptan değil, yöntemin imzasından da görülebilir: `istek` bir `String`
yerine bir `Optional<String>` döndürseydi, çağıranın eşleşmeme durumunu kontrol etmesi
zorunlu hâle gelirdi — tip sistemi eşleşmeyen yolu ayrı bir durum olarak taşırdı. Burada
seçilen imza bunu yapmıyor; "bulundu" ile "bulunamadı" aynı tipte, aynı biçimde geri
dönüyor. Sınırlayıcı ölçüm burada tam bu noktaya oturuyor: kusurun sessiz kalması kabın bir
zayıflığı değil, dönüş tipinin eşleşme durumunu ayırt etmemesinin doğrudan sonucu. Bir
yönlendirme kuralı yazan kişi bu ayrımı bilerek tasarlamak zorunda; aksi hâlde her yazım
hatası, hatasız görünen ama yanlış bir yanıtla sonuçlanır.

## Çakışan Uç Nokta: Kazanan Tarama Sırasına Bağlı

- **WV9.** `YedekDenetleyici`, `UrunDenetleyici` ile aynı yolu (`/urun/liste`) bildiriyor.
  Kap, aynı anahtara ikinci kez yazılan girdiyi öncekinin üzerine yazıyor; hangi girdinin
  "ikinci" olduğu `tara` çağrısına verilen argüman sırasına bağlı.
- **WV10.** Kap, çakışmayı denetlemiyor — ne bir uyarı basıyor ne bir istisna fırlatıyor.
  Kazananın kim olduğu yalnız iki kurulumu karşılaştırarak görülüyor.

`kapA`, `urun` ile `yedek`i bu sırayla tarıyor; `kapB` aynı iki denetleyiciyi ters sırayla.
`/urun/liste` isteğine verilen yanıt iki kurulumda **farklı**: `kapA`'da yedek denetleyicinin
yanıtı kazanıyor (son taranan o), `kapB`'de asıl denetleyicinin yanıtı kazanıyor (son taranan
o). Kaynak metinde hiçbir "kazanan budur" ifadesi yok; kazananı belirleyen tek şey `tara`
çağrısına verilen dizinin sırası.

Bu, önceki dersteki halka sıralamasıyla doğrudan karşıtlık kuruyor. Orada açıklamayla
bulunan halkaların sırası `oncelik` alanına göre belirleniyordu ve tarama sırasından tamamen
bağımsızdı — iki farklı yazım aynı sonucu üretiyordu. Burada aynı `tara` deseni kullanılıyor,
ama yönlendirme tablosu bir `Map` üzerine düz bir `put` ile kuruluyor ve **hiçbir eşitleyici
kural yok**. Fark, mekanizmanın kendisinde değil — ikisi de açıklama okuyan bir yansıma
taramasıdır — kabın çakışmayı çözecek açık bir kuralı olup olmamasında. Halka sıralamasında
`oncelik` alanı bu kuralı sağlıyordu; yönlendirme tablosunda böyle bir alan yok, bu yüzden
çakışma kaynaktaki hiçbir ifadeye değil, tarama sırasına düşüyor. **Bir açıklamanın
sıralamadan bağımsız olması otomatik gelmiyor; kabın o sıralamayı çözecek bir kuralı olması
gerekiyor, ve bu kural yoksa kazananı belirleyen şey kaynaktaki en son satır değil, kabın
kendi kayıt sırasıdır.**

## Elle Çağrılan Denetleyici Kap Kaydından Bağımsızdır

`urun.liste()` doğrudan çağrıldığında her zaman `"3 urun"` döner — bu, `UrunDenetleyici`
sınıfının kendi yönteminin davranışı, ve hiçbir kap kaydına bağlı değil. `kapA` üzerinden
`/urun/liste` isteği ise `"yedek liste"` döndürüyor, çünkü o kurulumda son taranan
`YedekDenetleyici` oldu. İki yanıt farklı, ve bu fark tam olarak dersin başındaki ayrımı
gösteriyor: `urun` nesnesinin kendi yöntemleri her zaman kendi mantığını çalıştırır, kap
üzerinden gelen bir yanıt ise kabın o anki kayıt durumuna bağlıdır. `UrunDenetleyici`
sınıfının kendisi hiç değişmedi, `liste()` yöntemi hâlâ aynı dizgeyi döndürüyor — değişen şey
kabın hangi yöntemi `/urun/liste` yoluna bağladığı. Bir denetleyici sınıfını birim testinde
elle çağırmak bu yüzden yalnız o sınıfın kendi mantığını doğruluyor; o sınıfın hangi yola
bağlı olduğunu, ve o yolun başka bir bileşenle çakışıp çakışmadığını doğrulamıyor. İki soru
ayrı: yöntem doğru mu, ve kap onu doğru yola mı bağladı — birincisini elle çağrı yanıtlıyor,
ikincisini yalnız kabın kendisi çalıştırılarak yanıtlanabiliyor.

Bu ayrımın kapsamı ilk derste görülenden biraz daha geniş: orada elle çağrı yalnız
varsayılan halkaları (`istek-kimligi`, `hata-yakalama`) kaçırıyordu, çünkü o davranış kabın
kendi kodunun bir parçasıydı. Burada kaçırılan şey bir varsayılan değil, kabın o an tuttuğu
**kayıt durumu** — hangi bileşenin hangi yolu son taradığı. Elle çağrı bu durumu hiç görmüyor
çünkü elle çağrı zaten kabı hiç sormuyor; nesnenin kendi belleğinde ne varsa onu çalıştırıyor.
İki tür bağımlılık da aynı kökten geliyor — kabın araya girip iş üzerinde bir karar vermesi —
ama biri kabın sabit davranışı, öteki kabın o an içinde bulunduğu duruma göre değişen bir
davranış.

## Özet

- Dört uç noktanın dördü de `@UcNokta` açıklamasından doğdu; yönlendirme tablosunu kuran
  hiçbir çağrı kaynakta yok, tabloyu `tara` yönteminin okuduğu açıklamalar kuruyor.
- Eşleşmeyen bir yol istendiğinde kap istisna fırlatmadı, sabit bir varsayılan yanıt
  döndürdü; bu, kursun sessiz kusur sınıfının bir örneği.
- İki bileşen aynı yolu bildirdiğinde kap çakışmayı denetlemedi; kazanan, tarama çağrısına
  verilen sınıf sırasına göre değişti.
- Bu, önceki dersteki halka sıralamasından farklı: orada açıklama sırası kabın kendi
  kuralıyla (öncelik) sabitti, burada yol çakışmasını çözecek bir kural olmadığı için
  sonuç kayıt sırasına bağlı kaldı.
- Bir denetleyici sınıfını elle çağırmak yalnız o sınıfın kendi mantığını sınar; hangi yola
  bağlı olduğunu ve bir çakışmada kazanıp kazanmadığını sınamaz.

## Sonraki Adım

Bu ders bir isteğin hangi yönteme ulaştığını ölçtü, ama o yönteme ulaşan verinin doğru
biçimde olup olmadığı hiç sorulmadı. Sıradaki ders bunu ele alıyor: bir alanın doğrulama
kuralının kaynakta bir çağrı mı yoksa bir açıklama mı olduğunu, ve kural bozulduğunda ortak
hata yanıtının hangi alanları nereden topladığını ölçüyor.
