Gün 2 / Web Layer & Runtime / Modül 04 — Spring MVC Internals
Ana Portal
Modül 04 Gün 2 Web Layer ~75 dk

Spring MVC Internals

Bu modülde bir HTTP isteğinin controller metoduna nasıl ulaştığını, controller’dan dönen cevabın JSON response’a nasıl dönüştüğünü ve Spring MVC’nin bu akışı hangi parçalarla yönettiğini göreceğiz. Amaç sadece @GetMapping kullanmak değil; request geldiğinde arkada çalışan zinciri okuyabilmek.

Anlatım
45 dk
MiniBank Case
25 dk
Kapanış
5 dk
Checkpoint
lab-04

Controller metodunun arkasındaki gerçek web akışını parçalarına ayırabileceksin.

1

Request akışını okuyacaksın

Bir isteğin DispatcherServlet üzerinden nasıl doğru controller metoduna yönlendirildiğini anlayacaksın.

2

JSON dönüşümünü göreceksin

Request body’nin Java objesine, Java objesinin JSON response’a nasıl dönüştüğünü öğreneceksin.

3

Custom argument resolver yazacaksın

MiniBank’ta CurrentUser bilgisini controller parametresine otomatik vereceksin.

Önce kelimeleri sadeleştirelim.

Spring MVC anlatımında çok fazla teknik terim var. Bu terimleri baştan sadeleştirirsek, akış daha rahat oturur.

DispatcherServlet

Basit: Spring MVC’nin ana giriş kapısıdır.

Neden önemli? HTTP request önce buraya gelir; sonra doğru controller metoduna gönderilir.

Controller

Basit: HTTP isteğini karşılayan sınıftır.

Neden önemli? API endpoint’lerimizin dış dünyaya açılan yüzüdür.

Handler

Basit: İsteği işleyecek controller metodu gibi düşünebilirsin.

Neden önemli? Spring MVC request için uygun handler’ı bulmaya çalışır.

HandlerMapping

Basit: Hangi URL hangi metoda gider, bunu bulur.

Neden önemli? /accounts/{iban} isteğinin hangi metoda düşeceği burada çözülür.

HandlerAdapter

Basit: Bulunan handler’ı çalıştıran adaptördür.

Neden önemli? Controller metodunun parametrelerini hazırlar ve metodu çağırır.

HttpMessageConverter

Basit: JSON ↔ Java obje dönüşümünü yapar.

Neden önemli? @RequestBody ve @ResponseBody davranışının merkezindedir.

Argument Resolver

Basit: Controller method parametrelerinin nasıl üretileceğini belirler.

Neden önemli? @PathVariable, @RequestBody, Principal gibi parametreler bu mantıkla çözülür.

ModelAndView

Basit: Klasik MVC’de model + view bilgisidir.

Neden önemli? REST API’de daha az görünür, ama Spring MVC’nin tarihsel ve mimari temelinde vardır.

Filter vs Interceptor

Basit: İkisi de request akışına araya girer.

Neden önemli? Filter servlet seviyesinde, Interceptor Spring MVC seviyesinde çalışır.

Bir request controller metoduna nasıl ulaşır?

Spring MVC’yi anlamanın en iyi yolu şu sorudan başlamak: Tarayıcıdan veya Postman’den bir istek gönderdim. Bu istek benim controller metoduma kadar hangi adımlardan geçiyor?

4.1 Spring MVC nedir?

Spring MVC, HTTP isteklerini Java metodlarına bağlayan web framework katmanıdır. Biz çoğu zaman @RestController, @GetMapping, @PostMapping, @RequestBody gibi annotation’ları görürüz. Bunlar kullanım tarafıdır. Bu modülde kullanımın arkasındaki mekanizmaya bakacağız.

En basit haliyle Spring MVC şunu yapar: Gelen isteğin URL ve HTTP method bilgisine bakar, uygun controller methodunu bulur, method parametrelerini hazırlar, methodu çağırır ve dönen cevabı HTTP response’a çevirir.

01
Request gelir
Client HTTP isteği gönderir.
02
DispatcherServlet alır
Spring MVC’nin merkezi kapısıdır.
03
Handler bulunur
URL hangi controller metoduna ait?
04
Parametreler hazırlanır
Path, query, body, header okunur.
05
Controller çalışır
Business akış service katmanına gider.
06
Response döner
Java obje JSON’a çevrilir.
Mental model

Controller metodu doğrudan çağrılan basit bir Java metodu gibi görünür; ama aslında öncesinde URL eşleştirme, parametre çözme, body parse etme, validation ve response dönüştürme gibi birçok adım çalışır.

4.2 DispatcherServlet — ana giriş kapısı

DispatcherServlet, Spring MVC’nin merkezindeki servlet’tir. Servlet container, yani çoğunlukla embedded Tomcat, HTTP isteğini alır ve Spring MVC’ye teslim eder. Spring MVC tarafında bu isteği ilk karşılayan parça DispatcherServlet olur.

Bu sınıfın görevini bir resepsiyon görevlisi gibi düşünebilirsin. İstek gelir, resepsiyon isteğin ne olduğunu anlamaya çalışır, doğru kişiye yönlendirir, cevap gelince de client’a geri döndürür. Kendisi business logic yazmaz; koordinasyon yapar.

DispatcherServlet ne yapar?

  • Request’i Spring MVC akışına alır.
  • Uygun handler’ı bulmak için HandlerMapping’lere sorar.
  • Handler’ı çalıştırmak için uygun HandlerAdapter’ı kullanır.
  • Exception oluşursa exception resolver zincirine devreder.
  • Response üretildikten sonra sonucu client’a döndürür.

Spring Boot kullandığımızda DispatcherServlet bean’ini genellikle biz oluşturmayız. spring-boot-starter-web dependency’si geldiğinde Spring Boot MVC auto-configuration üzerinden gerekli web altyapısını hazırlar. Modül 01’de gördüğümüz auto-configuration burada gerçek karşılığını bulur.

4.3 HandlerMapping — hangi URL hangi metoda gider?

Bir request geldiğinde Spring’in ilk kritik sorusu şudur: Bu URL ve HTTP method hangi controller metoduna ait? İşte HandlerMapping bu soruyu cevaplar.

Mesela aşağıdaki controller metodunu düşünelim:

AccountController.java
@RestController
@RequestMapping("/api/accounts")
public class AccountController {

    @GetMapping("/{iban}")
    public AccountResponse getAccount(@PathVariable String iban) {
        return accountService.getAccount(iban);
    }
}

GET /api/accounts/TR001 isteği geldiğinde Spring MVC bu mapping bilgisini önceden çıkarmış olur. Runtime’da URL ve HTTP method bilgisine bakarak bu request’in getAccount metoduna gitmesi gerektiğini bulur.

Senior dikkat noktası

Mapping çakışmaları production’da can yakabilir. Aynı path’i birden fazla controller’ın karşılamaya çalışması, belirsiz mapping hatalarına yol açar. Büyük projelerde URL tasarımı, controller paketlemesi ve endpoint standardı bu yüzden önemlidir.

4.4 HandlerAdapter — bulunan handler nasıl çalıştırılır?

HandlerMapping doğru handler’ı bulur, ama onu doğrudan çalıştırmaz. Çünkü Spring MVC’de farklı handler türleri olabilir. Controller methodu, functional endpoint, eski tip controller yapıları gibi farklı modelleri çalıştırmak için araya HandlerAdapter girer.

Günlük Spring Boot REST API geliştirmesinde en çok gördüğümüz model annotation tabanlı controller methodlarıdır. Bu durumda Spring, controller metodunun parametrelerini tek tek çözmek zorundadır. @PathVariable, @RequestParam, @RequestBody, @RequestHeader gibi parametrelerin her biri farklı kaynaklardan gelir.

@PathVariable

URL içindeki değişken parçadan değer alır. Örnek: /{iban}

@RequestParam

Query string üzerinden değer alır. Örnek: ?page=0

@RequestBody

HTTP body içindeki JSON verisini Java objesine çevirir.

@RequestHeader

HTTP header içinden değer okur. Örnek: X-Correlation-Id

4.5 HttpMessageConverter — JSON nerede devreye girer?

Spring MVC’de request body’nin Java objesine dönüşmesi ve Java objesinin JSON response’a dönüşmesi HttpMessageConverter mekanizmasıyla yapılır. REST API geliştirirken en sık kullanılan converter genellikle Jackson tabanlı JSON converter’dır.

Örneğin client aşağıdaki JSON’u gönderir:

HTTP Request Body
{
  "iban": "TR001",
  "initialBalance": 1000
}

Controller metodunda ise bunu Java objesi olarak görürüz:

AccountController.java
@PostMapping
public ResponseEntity<AccountResponse> createAccount(
        @RequestBody AccountCreateRequest request) {
    return ResponseEntity.ok(accountService.createAccount(request));
}

Bu dönüşümün manuel olarak bizim tarafımızdan yapılmaması büyük kolaylıktır. Ama bu kolaylık görünmez olduğunda bazı problemleri anlamak zorlaşır: yanlış tarih formatı, beklenmeyen enum değeri, eksik field, büyük/küçük harf farkı, response’ta istenmeyen alanların çıkması gibi konular çoğu zaman JSON dönüşüm katmanıyla ilgilidir.

MiniBank bağlantısı

MiniBank’ta Account, Customer ve Transfer API’leri yazarken request/response DTO’larını JSON’a çevirmeyi Spring MVC ve Jackson üstlenecek. Biz bu modülde dönüşümün nerede gerçekleştiğini netleştiriyoruz; sonraki modülde DTO ve validation tarafını büyüteceğiz.

4.6 Argument Resolver — controller parametresini kim hazırlıyor?

Spring MVC’nin en güçlü ama çoğu zaman fark edilmeyen parçalarından biri HandlerMethodArgumentResolver mekanizmasıdır. Controller metoduna yazdığımız parametrelerin nasıl üretileceğini bu mekanizma belirler.

Örneğin @PathVariable String iban parametresi URL’den gelir. @RequestBody AccountCreateRequest request parametresi JSON body’den gelir. Peki kendi özel parametremizi üretmek istersek ne olur?

MiniBank’ta her request’te header’dan gelen kullanıcı bilgisini controller metodunda doğrudan almak isteyebiliriz:

Hedef kullanım
@GetMapping("/me/accounts")
public List<AccountResponse> myAccounts(CurrentUser currentUser) {
    return accountQueryService.findAccountsOf(currentUser.customerNo());
}

Burada CurrentUser nesnesini controller içinde manuel header okuyarak oluşturmak istemiyoruz. Bu iş her controller’da tekrarlanırsa kod kirlenir. Bunun yerine Spring MVC’ye şunu öğretiriz: “Eğer controller metodunda CurrentUser parametresi görürsen, bunu request header’dan üret.”

Bu modülün case fikri

MiniBank için custom argument resolver yazacağız. Böylece web layer’da tekrarlayan header okuma kodunu merkezi hale getireceğiz.

MiniBank Case 04 — CurrentUser Argument Resolver

Bu case’de MiniBank controller’larında tekrar eden “header’dan kullanıcı bilgisi okuma” ihtiyacını Spring MVC’nin argument resolver mekanizmasıyla merkezi hale getireceğiz. Görev listesi, doğrulama komutları, kod adımları ve kabul kriterleri Lab Dashboard'da.

Başlangıç
lab-04-start
Tamamlanmış
lab-04-complete
Checkpoint kuralı

Lab sırasında geride kalırsan lab-04-complete branch'ine geçip herkesle aynı checkpoint'ten devam edebilirsin.

Bu modülden akılda kalması gerekenler

1
Spring MVC request akışı merkezi bir zincirdir.

Request önce DispatcherServlet’e gelir; sonra mapping, adapter, argument resolver ve converter gibi parçalar devreye girer.

2
Controller metodu tek başına çalışmaz.

Parametrelerin hazırlanması, body dönüşümü ve response üretimi Spring MVC altyapısı tarafından yönetilir.

3
HttpMessageConverter REST API’nin görünmeyen kahramanıdır.

JSON request ve response dönüşümünün büyük kısmını bizim yerimize yapar.

4
Argument resolver tekrar eden controller kodunu azaltır.

Current user, tenant, correlation id gibi request context bilgileri merkezi olarak üretilebilir.

5
Web layer tasarımı sonraki modüllerin temelidir.

DTO, validation, exception handling ve security konuları bu MVC akışının üzerine oturur.

5 kısa kontrol sorusu

1. DispatcherServlet’in temel görevi nedir?

Spring MVC’ye gelen request’i karşılamak, doğru handler’ı buldurmak, handler’ı çalıştırmak ve response akışını koordine etmektir.

2. HandlerMapping hangi soruyu cevaplar?

“Bu URL ve HTTP method hangi controller metoduna gitmeli?” sorusunu cevaplar.

3. HttpMessageConverter ne işe yarar?

HTTP body ile Java objeleri arasında dönüşüm yapar. REST API’de JSON ↔ Java obje dönüşümü çoğunlukla bu mekanizmayla yapılır.

4. Argument Resolver neden kullanılır?

Controller method parametrelerinin nasıl oluşturulacağını merkezi hale getirmek için kullanılır. Örneğin CurrentUser parametresini header’dan üretmek için.

5. Custom argument resolver yazmak controller kodunu nasıl iyileştirir?

Header okuma, request context oluşturma veya tekrar eden parametre hazırlama kodlarını controller’dan çıkarır; daha temiz ve tutarlı bir web layer sağlar.