← Back to list

Java ve Spring Boot ile Modern Backend Geliştirme

Java, yıllardır kurumsal yazılım geliştirme dünyasının en çok tercih edilen programlama dillerinden biri olmaya devam ediyor. Özellikle…

Gazihan izci · 2026-07-22 15:44 · 0 claps · 28.4 min read
#java-spring-boot #java-backend-development #rest-api #software-development #backend-development
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🛠️ · Crafts & DIY

Java ve Spring Boot ile Modern Backend Geliştirme

Java, yıllardır kurumsal yazılım geliştirme dünyasının en çok tercih edilen programlama dillerinden biri olmaya devam ediyor. Özellikle bankacılık, e-ticaret, sağlık ve kamu sistemleri gibi yüksek ölçekli uygulamalarda Java tabanlı backend servisleri yaygın olarak kullanılıyor.

Ancak Java öğrenmeye başlayan birçok geliştirici için “Backend geliştirme tam olarak nedir?”, “Spring Boot neden kullanılır?”, “REST API nasıl çalışır?” veya “Gerçek bir backend projesi nasıl geliştirilir?” gibi sorular oldukça kafa karıştırıcı olabiliyor.

Bu yazıda, Java ile backend geliştirme sürecini temelden başlayarak adım adım ele alacağız. Önce backend mimarisini ve istemci-sunucu iletişimini inceleyecek, ardından Spring Framework ve Spring Boot’un sağladığı kolaylıklara değineceğiz. Devamında katmanlı mimari, REST API geliştirme, veritabanı işlemleri, JPA/Hibernate, güvenlik, JWT tabanlı kimlik doğrulama, hata yönetimi, test süreçleri ve Docker ile uygulamanın dağıtımına kadar modern bir backend uygulamasında ihtiyaç duyulan temel konuları örneklerle inceleyeceğiz.

Backend Nedir?

Back-end, bir uygulamanın kullanıcılar tarafından görülmeyen; verilerin işlendiği, hesaplamaların yapıldığı ve sistem güvenliğinin sağlandığı sunucu tarafı altyapısıdır.

Kullanıcının görmediği tarafta neler olur?

Kullanıcı bir işlem gerçekleştirdiğinde backend gelen isteği karşılar, kimlik doğrulama ve yetkilendirme kontrollerini yapar, verileri doğrular, iş kurallarını uygular ve veritabanı ile iletişim kurar. Gerekirse harici servislerle (ödeme, e-posta, SMS vb.) haberleşir, işlemleri loglar ve son olarak işlemin sonucunu frontend’e gönderir.

Frontend ile Backend Arasındaki Fark

Frontend, uygulamanın tarayıcı veya telefonda çalışan kısmıdır; ekran tasarımlarını, buton tıklamalarını ve görsel düzeni yöneterek veriyi sana şık bir şekilde sunar. Backend ise uygulamanın uzaktaki güçlü bilgisayarlarda (sunucularda) çalışan kısmıdır; gelen istekleri karşılar, şifreni kontrol eder, veri tabanından bilgiyi çekip işler ve cevabı frontend’e geri gönderir.

Frontend ve Backend Nasıl İletişim Kurar?

Frontend ve backend, internet üzerinden HTTP/HTTPS istekleri aracılığıyla iletişim kurar. Kullanıcı bir butona tıkladığında veya bir form gönderdiğinde frontend, backend’e bir HTTP Request gönderir. Backend bu isteği işler, gerekli kontrolleri yapar, veritabanı veya diğer servislerle iletişim kurar ve sonucu HTTP Response olarak frontend’e geri döndürür. Frontend ise bu yanıtı işleyerek kullanıcıya ekranda gösterir. Günümüzde bu iletişim çoğunlukla REST API veya GraphQL API üzerinden JSON formatında veri alışverişi yapılarak gerçekleştirilir.

Spring Boot Nedir?

Spring Boot, Java ile modern backend uygulamaları geliştirmeyi kolaylaştıran, Spring Framework üzerine kurulmuş açık kaynaklı bir geliştirme çatısıdır. Gerekli yapılandırmaların büyük bölümünü otomatik olarak yaptığı için geliştiricilerin karmaşık ayarlarla uğraşmak yerine doğrudan uygulama geliştirmesine olanak tanır. REST API, web servisleri, mikroservisler ve kurumsal uygulamalar geliştirmek için yaygın olarak tercih edilir. Özellikle hızlı geliştirme süreci, gömülü sunucu desteği ve geniş ekosistemi sayesinde Java backend dünyasının en popüler teknolojilerinden biridir.

Spring Boot Projesi Oluşturma

Spring Boot ile uygulama geliştirmeye başlamadan önce doğru bir proje yapısı oluşturmak oldukça önemlidir. Kullanılacak Java sürümü, build aracı ve bağımlılıkların doğru seçilmesi; geliştirme sürecini kolaylaştırırken uygulamanın sürdürülebilir ve yönetilebilir olmasını da sağlar.

Günümüzde Spring Boot projeleri genellikle Spring Initializr üzerinden oluşturulur. Spring Initializr, gerekli yapılandırmaları otomatik olarak hazırlayan ve geliştiricinin birkaç dakika içinde çalışmaya başlamasını sağlayan resmi proje oluşturma aracıdır. Bu sayede Maven veya Gradle yapılandırmaları, temel proje dosyaları ve seçilen bağımlılıklar otomatik olarak eklenir.

Spring Initializr

Spring Initializr, Spring Boot projeleri oluşturmak için kullanılan resmi araçtır. Proje oluşturma sırasında ihtiyaç duyulan temel ayarlar birkaç adımda belirlenebilir ve hazır proje indirilebilir.

Proje oluştururken aşağıdaki alanlar karşımıza çıkar:

  • Project: Maven veya Gradle
  • Language: Java
  • Spring Boot Version: Kullanılacak Spring Boot sürümü
  • Group: Projenin grup kimliği (örneğin com.example)
  • Artifact: Proje adı
  • Name: Uygulamanın görünen adı
  • Package Name: Ana Java paketi
  • Packaging: Jar veya War
  • Java Version: Kullanılacak JDK sürümü
  • Dependencies: Projede kullanılacak Spring modülleri

Bu bilgiler doldurulduktan sonra proje indirilebilir ve tercih edilen geliştirme ortamında açılarak geliştirmeye başlanabilir.

Maven mi, Gradle mı?

Spring Boot projelerinde bağımlılık yönetimi ve uygulamanın derlenmesi için en yaygın kullanılan iki araç Maven ve Gradle’dır.

Mave

Maven, XML tabanlı yapılandırma kullanan, uzun yıllardır Java ekosisteminde tercih edilen bir build aracıdır. Tüm bağımlılıklar pom.xml dosyasında tanımlanır.

Avantajları:

  • Öğrenmesi kolaydır.
  • Kararlı ve olgun bir yapıya sahiptir.
  • Kurumsal projelerde oldukça yaygın kullanılır.
  • Geniş dokümantasyon desteği bulunur.

Spring Boot projelerinde en sık kullanılan build aracıdır.

Gradle

Gradle ise Groovy veya Kotlin DSL kullanan daha modern bir build aracıdır. Yapılandırmalar build.gradle veya build.gradle.kts dosyasında gerçekleştirilir.

Avantajları:

  • Daha hızlı derleme süresi sunabilir.
  • Daha esnek yapılandırmalara izin verir.
  • Büyük projelerde gelişmiş özelleştirme imkânı sağlar.

Yeni başlayan geliştiriciler için Maven daha anlaşılır olsa da, her iki araç da Spring Boot tarafından tam olarak desteklenmektedir.

Java Sürümü Seçimi

Spring Boot projesi oluştururken dikkat edilmesi gereken en önemli noktalardan biri kullanılacak Java sürümüdür.

Genellikle LTS (Long Term Support) sürümleri tercih edilir. LTS sürümleri uzun süre güvenlik güncellemeleri aldığı için üretim ortamlarında daha güvenilir bir seçenektir.

Günümüzde en yaygın kullanılan sürümler:

  • Java 17 (LTS)
  • Java 21 (LTS)

Yeni bir Spring Boot projesine başlanıyorsa mümkün olduğunca güncel LTS sürümünün tercih edilmesi önerilir.

Dependency (Bağımlılık) Seçimi

Spring Boot’un en güçlü özelliklerinden biri, ihtiyaç duyulan kütüphaneleri Starter Dependency yapısı sayesinde tek bir bağımlılıkla projeye ekleyebilmesidir.

Örneğin REST API geliştirmek isteyen bir geliştiricinin onlarca farklı kütüphaneyi tek tek eklemesine gerek kalmaz. Bunun yerine yalnızca ilgili starter paketi eklemesi yeterlidir.

En sık kullanılan bağımlılıklar şunlardır:

Spring Web

REST API geliştirmek için kullanılır.

İçerisinde;

  • Spring MVC
  • Embedded Tomcat
  • Jackson JSON desteği

gibi birçok bileşen hazır olarak gelir.

Spring Data JPA

Veritabanı işlemlerini kolaylaştırır.

Sağladığı başlıca özellikler:

  • ORM desteği
  • Repository yapısı
  • CRUD işlemleri
  • JPQL desteği

Validation

Kullanıcıdan gelen verilerin doğrulanmasını sağlar.

Örneğin:

  • Boş geçilemez alanlar
  • Minimum karakter uzunluğu
  • E-posta formatı kontrolü

gibi doğrulamalar kolayca uygulanabilir.

Spring Security

Kimlik doğrulama ve yetkilendirme işlemlerini yönetmek için kullanılır.

JWT, OAuth2 ve farklı güvenlik mekanizmaları Spring Security ile entegre şekilde kullanılabilir.

Lombok

Tekrarlayan kodların azaltılmasını sağlar.

Getter, Setter, Constructor ve Builder gibi yapılar otomatik olarak oluşturulabilir.

MySQL Driver / PostgreSQL Driver

Spring Boot’un ilgili veritabanına bağlanabilmesi için gerekli sürücülerdir.

Kullanılan veritabanına göre uygun driver projeye eklenmelidir.

Spring Boot Proje Yapısı

Spring Boot projeleri belirli bir klasör düzenine sahiptir. Bu yapı, kodun okunabilirliğini artırır ve katmanlı mimarinin uygulanmasını kolaylaştırır.

src
 ├── main
 │    ├── java
 │    │      └── com.example.demo
 │    │             ├── controller
 │    │             ├── service
 │    │             ├── repository
 │    │             ├── entity
 │    │             ├── dto
 │    │             ├── config
 │    │             ├── exception
 │    │             └── DemoApplication.java
 │    └── resources
 │           ├── application.properties
 │           ├── static
 │           └── templates
 └── test

Bu yapıda her klasör belirli bir sorumluluğa sahiptir:

  • controller: HTTP isteklerini karşılayan sınıfları içerir.
  • service: İş kurallarının uygulandığı katmandır.
  • repository: Veritabanı işlemlerini gerçekleştirir.
  • entity: Veritabanı tablolarını temsil eden sınıfları barındırır.
  • dto: Katmanlar arasında veri taşımak için kullanılan nesneleri içerir.
  • config: Uygulamanın yapılandırma sınıflarını barındırır.
  • exception: Özel hata sınıfları ve global hata yönetimi bu klasörde yer alır.
  • resources: Yapılandırma dosyaları, statik içerikler ve uygulama kaynaklarını içerir.
  • test: Birim ve entegrasyon testlerinin bulunduğu klasördür.

Doğru yapılandırılmış bir proje dizini, uygulama büyüdükçe kodun daha okunabilir, yönetilebilir ve sürdürülebilir olmasını sağlar. Bu nedenle proje geliştirmeye başlamadan önce klasör yapısının doğru planlanması, ilerleyen aşamalarda önemli avantajlar sağlayacaktır.

SpringBootApplication

Spring Boot ile yeni bir proje oluşturduğunuzda karşınıza çıkan ilk sınıf genellikle aşağıdaki gibi görünür:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

İlk bakışta yalnızca birkaç satırdan oluşan bu kod oldukça basit görünebilir. Ancak arka planda Spring Boot’un uygulamayı çalıştırabilmesi için gerçekleştirdiği onlarca işlem bulunmaktadır. Uygulamanın ayağa kaldırılması, gerekli bileşenlerin oluşturulması, gömülü web sunucusunun başlatılması ve yapılandırmaların yüklenmesi gibi birçok süreç bu sınıf sayesinde otomatik olarak yönetilir.

Bu nedenle Spring Boot öğrenirken yalnızca @SpringBootApplication anotasyonunu kullanmayı bilmek yeterli değildir. Bu anotasyonun arka planda hangi işlemleri gerçekleştirdiğini anlamak, Spring Boot'un çalışma mantığını kavrayabilmek açısından oldukça önemlidir.

SpringBootApplication Nedir?

@SpringBootApplication, Spring Boot uygulamalarının başlangıç noktasıdır. Tek bir anotasyon gibi görünmesine rağmen aslında üç farklı anotasyonun birleşiminden oluşur.

@SpringBootApplication
        │
        ├────────► @Configuration
        │
        ├────────► @EnableAutoConfiguration
        │
        └────────► @ComponentScan

Dolayısıyla aşağıdaki kullanım ile tamamen aynı görevi yerine getirir.

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoApplication {

}

Spring Boot, geliştiricilerin bu anotasyonları tek tek eklemek zorunda kalmaması için bunları @SpringBootApplication altında bir araya getirmiştir. Böylece uygulamanın başlangıç yapılandırması daha sade ve okunabilir hale gelir.

Spring Boot Uygulaması Başlatıldığında Neler Olur?

Uygulama çalıştırıldığında yalnızca main() metodunun çağrıldığını düşünmek doğru değildir. Aslında Spring Boot oldukça kapsamlı bir başlatma süreci yürütür.

main()
      │
      ▼
SpringApplication.run()
      │
      ▼
ApplicationContext oluşturulur
      │
      ▼
@ComponentScan çalıştırılır
      │
      ▼
Bean'ler oluşturulur
      │
      ▼
Auto Configuration uygulanır
      │
      ▼
Embedded Tomcat başlatılır
      │
      ▼
Uygulama HTTP isteklerini karşılamaya hazır hale gelir

Bu süreç boyunca Spring Boot, geliştiricinin manuel olarak yapması gereken birçok işlemi otomatik olarak gerçekleştirir. İşte Spring Boot’un en önemli avantajlarından biri de tam olarak budur.

Configuration

@SpringBootApplication anotasyonunun içerdiği ilk yapı @Configuration anotasyonudur.

Bu anotasyon, ilgili sınıfın Spring için bir konfigürasyon sınıfı olduğunu belirtir. Başka bir ifadeyle, Spring Container içerisinde yönetilecek Bean’lerin tanımlanacağı sınıflar @Configuration ile işaretlenir.

Örneğin aşağıdaki yapı incelendiğinde, ModelMapper nesnesinin Spring tarafından oluşturulduğu görülmektedir.

@Configuration
public class AppConfig {
@Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
}

Burada dikkat edilmesi gereken nokta, new ModelMapper() ifadesinin yalnızca bir kez çalıştırılmasıdır. Spring oluşturduğu nesneyi kendi Container'ı içerisinde saklar ve uygulamanın ihtiyaç duyduğu her yerde aynı nesneyi kullanır.

Böylece her sınıfta tekrar tekrar nesne oluşturulmasına gerek kalmaz.

Bu süreci aşağıdaki şema özetlemektedir.

@Configuration
       │
       ▼
@Bean metotları bulunur
       │
       ▼
Nesneler oluşturulur
       │
       ▼
ApplicationContext içerisine eklenir
       │
       ▼
Uygulama boyunca aynı Bean kullanılır

EnableAutoConfiguration

Spring Boot’u klasik Spring projelerinden ayıran en önemli özelliklerden biri Auto Configuration mekanizmasıdır.

Eski Spring uygulamalarında geliştiriciler; web sunucusunu, DispatcherServlet’i, JSON dönüştürücülerini, veri kaynağını ve birçok bileşeni manuel olarak yapılandırmak zorundaydı.

Spring Boot ise projeye eklenen bağımlılıkları analiz ederek gerekli yapılandırmaları otomatik olarak gerçekleştirir.

Örneğin projeye yalnızca aşağıdaki bağımlılığı eklediğimizi düşünelim.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Bu tek bağımlılık sayesinde Spring Boot;

  • Embedded Tomcat’i yapılandırır.
  • Spring MVC desteğini etkinleştirir.
  • DispatcherServlet’i oluşturur.
  • JSON dönüşümleri için Jackson kütüphanesini yapılandırır.
  • REST API geliştirmeye uygun ortamı hazır hale getirir.

Geliştirici bunların hiçbirini manuel olarak yapmak zorunda kalmaz.

Auto Configuration mekanizmasının çalışma mantığı aşağıdaki şekilde özetlenebilir.

Projeye eklenen Dependency
            │
            ▼
Spring Boot bağımlılıkları analiz eder
            │
            ▼
Uygun Auto Configuration sınıfları yüklenir
            │
            ▼
Gerekli Bean'ler oluşturulur
            │
            ▼
Uygulama çalışmaya hazır hale gelir

Bu yapı sayesinde Spring Boot, “Convention over Configuration” yaklaşımını benimser. Yani çoğu durumda geliştiricinin ayrıntılı yapılandırmalar yapmasına gerek kalmadan uygulama varsayılan ayarlarla çalışabilir.

ComponentScan

Spring Boot’un en önemli özelliklerinden biri de uygulamadaki bileşenleri otomatik olarak bulabilmesidir.

Normal şartlarda Java’da bir nesneyi kullanabilmek için aşağıdaki gibi manuel olarak oluşturmamız gerekir.

UserService userService = new UserService();

Ancak Spring Boot’ta çoğu zaman böyle bir kod yazmayız.

Örneğin aşağıdaki sınıfları ele alalım.

@Service
public class UserService {
}
@Repository
public class UserRepository {
}
@RestController
public class UserController {
}

Bu sınıfların hiçbirinde new anahtar kelimesi kullanılmamasına rağmen Spring Boot uygulama açılırken bu sınıfları otomatik olarak bulur ve Bean haline getirir.

Bunun nedeni @ComponentScan anotasyonudur.

Uygulama başlatıldığında Spring Boot, ana sınıfın bulunduğu paketi ve alt paketlerini tarayarak aşağıdaki anotasyonlara sahip sınıfları tespit eder.

  • @Component
  • @Service
  • @Repository
  • @Controller
  • @RestController
  • @Configuration

Bulduğu her uygun sınıfı Spring Container içerisine ekleyerek uygulamanın ihtiyaç duyduğu her yerde kullanılabilir hale getirir.

Bu süreci aşağıdaki diyagram özetlemektedir.

Application Başlıyor
        │
        ▼
@ComponentScan
        │
        ▼
Paketler taranıyor
        │
        ▼
@Component
@Service
@Repository
@RestController
@Configuration
        │
        ▼
Bean oluşturuluyor
        │
        ▼
ApplicationContext'e ekleniyor

Bu mekanizma sayesinde Spring Boot, bağımlılık yönetimini büyük ölçüde otomatik hale getirerek kod tekrarını önemli ölçüde azaltır.

SpringApplication.run()

Spring Boot uygulamasının gerçekten başlatıldığı nokta ise aşağıdaki satırdır.

SpringApplication.run(DemoApplication.class, args);

Bu metodun görevi yalnızca uygulamayı çalıştırmak değildir. Arka planda birbirini takip eden birçok işlemi yönetir.

Başlıca gerçekleştirilen işlemler şunlardır:

  1. Spring uygulaması başlatılır.
  2. ApplicationContext oluşturulur.
  3. application.properties veya application.yml dosyaları okunur.
  4. @ComponentScan çalıştırılarak bileşenler taranır.
  5. Gerekli Bean’ler oluşturulur.
  6. Auto Configuration mekanizması devreye girer.
  7. Gömülü web sunucusu (Tomcat, Jetty veya Undertow) başlatılır.
  8. DispatcherServlet yapılandırılır.
  9. Uygulama gelen HTTP isteklerini karşılamaya hazır hale gelir.

Bu sürecin genel akışı aşağıdaki diyagramda gösterilmektedir.

main()
      │
      ▼
SpringApplication.run()
      │
      ▼
Configuration yüklenir
      │
      ▼
Component Scan yapılır
      │
      ▼
Bean'ler oluşturulur
      │
      ▼
Auto Configuration uygulanır
      │
      ▼
Embedded Server başlatılır
      │
      ▼
REST API çalışmaya başlar

Bean Yönetimi

Spring Boot’un en önemli özelliklerinden biri, uygulama içerisinde oluşturulan nesnelerin yaşam döngüsünü ve yönetimini geliştirici yerine kendisinin üstlenmesidir. Klasik Java uygulamalarında bir nesne oluşturmak için new anahtar kelimesi kullanılırken, Spring Boot projelerinde bu görev büyük ölçüde Spring IoC (Inversion of Control) Container tarafından gerçekleştirilir.

Spring tarafından oluşturulan, yönetilen ve yaşam döngüsü kontrol edilen nesnelere Bean adı verilir. Spring Boot’un Dependency Injection, katmanlı mimari ve gevşek bağlı (loosely coupled) uygulama geliştirme anlayışının temelinde Bean kavramı yer alır.

Bean Nedir?

Bean, Spring Container tarafından oluşturulan ve yönetilen Java nesnesidir.

Örneğin aşağıdaki sınıfı ele alalım.

@Service
public class UserService {
}

Bu sınıf @Service anotasyonu ile işaretlendiği için uygulama başlatıldığında Spring tarafından otomatik olarak oluşturulur ve ApplicationContext içerisine eklenir.

Artık uygulamanın herhangi bir yerinde bu sınıfı aşağıdaki gibi kullanabiliriz.

@RequiredArgsConstructor
@RestController
public class UserController {
    private final UserService userService;
}

Burada dikkat edilmesi gereken nokta, UserService nesnesinin geliştirici tarafından oluşturulmamasıdır. Spring Container bu nesneyi tek bir kez oluşturur ve ihtiyaç duyulan sınıflara otomatik olarak enjekte eder.

Bean Nasıl Oluşur?

Spring Boot uygulaması çalıştırıldığında @ComponentScan mekanizması devreye girer ve belirlenen paketleri tarar.

Bu tarama sırasında aşağıdaki anotasyonlardan birine sahip olan sınıflar tespit edilir:

  • @Component
  • @Service
  • @Repository
  • @Controller
  • @RestController
  • @Configuration

Bulunan sınıflar Bean olarak oluşturulur ve Spring Container içerisine eklenir.

Bu süreci aşağıdaki şema özetlemektedir.

Spring Boot Başlıyor
        │
        ▼
@ComponentScan
        │
        ▼
Paketler taranıyor
        │
        ▼
@Service
@Repository
@RestController
@Component
        │
        ▼
Bean oluşturuluyor
        │
        ▼
ApplicationContext içerisine ekleniyor

Bazı durumlarda ise Bean’ler geliştirici tarafından manuel olarak oluşturulabilir. Bunun için @Configuration ve @Bean anotasyonları kullanılır.

@Configuration
public class AppConfig {
    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
}

Bu örnekte ModelMapper nesnesi Spring Container tarafından yönetilen bir Bean haline gelir.

Bean Scope

Bir Bean oluşturulduktan sonra Spring bu nesnenin kaç adet oluşturulacağını ve hangi yaşam alanında kullanılacağını Scope kavramı ile belirler.

Spring Boot’ta en sık kullanılan Bean Scope türleri şunlardır:

ScopeAçıklamaSingletonTüm uygulama boyunca tek bir nesne oluşturulur. (Varsayılan)PrototypeHer kullanımda yeni bir nesne oluşturulur.RequestHer HTTP isteği için yeni bir Bean oluşturulur.SessionHer kullanıcı oturumu için ayrı bir Bean oluşturulur.

Singleton Scope

Varsayılan Bean Scope türüdür.

@Service
public class UserService {
}

Yukarıdaki sınıf yalnızca bir kez oluşturulur.

Tüm Controller ve Service sınıfları aynı UserService nesnesini kullanır.

Controller A ───────┐
                    │
Controller B ───────┼────► UserService
                    │
Controller C ───────┘

Bu yaklaşım bellek kullanımını azaltır ve performansı artırır.

Prototype Scope

Bazı durumlarda her kullanım için yeni bir nesne oluşturulması istenebilir.

@Component
@Scope("prototype")
public class ReportGenerator {
}

Bu durumda Spring her istekte yeni bir ReportGenerator nesnesi oluşturur.

İstek 1 ─────► ReportGenerator #1
İstek 2 ─────► ReportGenerator #2
İstek 3 ─────► ReportGenerator #3

Prototype Scope, durumu (state) değişen nesneler için tercih edilebilir.

Bean Lifecycle (Bean Yaşam Döngüsü)

Bir Bean yalnızca oluşturulup kullanılmaz. Spring tarafından belirli aşamalardan geçirilerek yönetilir.

Bean’in yaşam döngüsü genel olarak aşağıdaki gibidir.

Bean Tanımlanır
        │
        ▼
Bean Oluşturulur
        │
        ▼
Dependency Injection
        │
        ▼
@PostConstruct
        │
        ▼
Bean Kullanılır
        │
        ▼
@PreDestroy
        │
        ▼
Bean Bellekten Kaldırılır

Bu süreç sayesinde Spring, Bean’in oluşturulmasından uygulama kapanana kadar tüm yaşam döngüsünü kontrol eder.

PostConstruct

Bean oluşturulduktan ve tüm bağımlılıkları enjekte edildikten sonra çalıştırılması gereken işlemler için kullanılır.

@Service
public class UserService {
@PostConstruct
    public void init() {
        System.out.println("UserService hazır.");
    }
}

Veritabanı önbelleğinin hazırlanması veya başlangıç verilerinin yüklenmesi gibi işlemler burada gerçekleştirilebilir.

PreDestroy

Uygulama kapanmadan hemen önce çalıştırılır.

@Service
public class UserService {
@PreDestroy
    public void destroy() {
        System.out.println("Kaynaklar temizleniyor.");
    }
}

Dosya bağlantılarının kapatılması, açık bağlantıların sonlandırılması veya geçici kaynakların temizlenmesi gibi işlemler için kullanılabilir.

Dependency Injection (DI)

Modern yazılım geliştirme süreçlerinde en önemli tasarım prensiplerinden biri gevşek bağlılık (Loose Coupling) ilkesidir. Sınıfların birbirine doğrudan bağımlı olması, kodun test edilmesini, bakımını ve geliştirilmesini zorlaştırır. Spring Boot, bu problemi Dependency Injection (DI) mekanizması ile çözer.

Dependency Injection, bir sınıfın ihtiyaç duyduğu bağımlılıkların (dependency) sınıfın kendisi tarafından oluşturulması yerine Spring Container tarafından oluşturularak ilgili sınıfa verilmesi işlemidir.

Basit bir ifadeyle; bir nesnenin başka bir nesneyi new anahtar kelimesi ile oluşturması yerine, bu nesnenin Spring tarafından hazır olarak sağlanmasıdır.

Örneğin aşağıdaki kullanım Dependency Injection değildir.

public class UserService {
    private UserRepository repository = new UserRepository();
}

Bu yapıda UserService, UserRepository sınıfına doğrudan bağımlıdır. Repository değiştiğinde veya test ortamında farklı bir yapı kullanılmak istendiğinde UserService sınıfının da değiştirilmesi gerekir.

Spring Boot’ta ise aynı işlem şu şekilde gerçekleştirilir.

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
}

Bu örnekte UserRepository nesnesi Spring Container tarafından oluşturulur ve UserService sınıfına otomatik olarak enjekte edilir.

Bu yaklaşım sayesinde sınıflar birbirine daha az bağımlı hale gelir ve uygulama daha esnek bir mimariye kavuşur.

Dependency Injection Nasıl Çalışır?

Spring Boot uygulaması başlatıldığında öncelikle Bean’ler oluşturulur. Daha sonra Spring, hangi sınıfın hangi Bean’e ihtiyaç duyduğunu analiz eder ve gerekli bağımlılıkları otomatik olarak ilgili sınıflara aktarır.

Bu süreç aşağıdaki şekilde özetlenebilir.

Spring Boot Başlıyor
        │
        ▼
Bean'ler Oluşturuluyor
        │
        ▼
Bağımlılıklar Analiz Ediliyor
        │
        ▼
Gerekli Bean Bulunuyor
        │
        ▼
İlgili Sınıfa Enjekte Ediliyor

Bu mekanizma sayesinde geliştiricinin nesne oluşturma ve yönetme yükü büyük ölçüde Spring tarafından üstlenilir.

Constructor Injection

Spring Boot projelerinde en çok tercih edilen ve önerilen Dependency Injection yöntemi Constructor Injection’dır.

Bu yöntemde bağımlılıklar sınıfın constructor metodu aracılığıyla alınır.

@Service
public class UserService {
private final UserRepository repository;
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}

Spring, UserRepository Bean'ini oluşturur ve constructor çalıştırılırken otomatik olarak parametreye gönderir.

Lombok kullanıldığında ise aynı işlem çok daha kısa yazılabilir.

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
}

Avantajları

  • Değiştirilemez (final) alanlar kullanılabilir.
  • Null olma ihtimali azalır.
  • Test yazımı daha kolaydır.
  • Spring tarafından önerilen yöntemdir.
  • Zorunlu bağımlılıklar açık şekilde belirtilir.

Bu nedenlerle yeni Spring Boot projelerinde mümkün olduğunca Constructor Injection tercih edilmelidir.

Setter Injection

Setter Injection yönteminde bağımlılıklar setter metotları üzerinden enjekte edilir.

@Service
public class UserService {
private UserRepository repository;
    @Autowired
    public void setRepository(UserRepository repository) {
        this.repository = repository;
    }
}

Bu yöntem genellikle isteğe bağlı (optional) bağımlılıklar için kullanılabilir.

Ancak bağımlılık sonradan değiştirilebildiği için Constructor Injection kadar güvenli değildir.

Field Injection

Field Injection, bağımlılığın doğrudan değişken üzerine enjekte edilmesidir.

@Service
public class UserService {
@Autowired
    private UserRepository repository;
}

Kod yazımı oldukça kısa olmasına rağmen günümüzde önerilen bir yöntem değildir.

Dezavantajları

  • Test yazmayı zorlaştırır.
  • final alan kullanılamaz.
  • Bağımlılıklar sınıfın dışından görünmez.
  • Spring’e olan bağımlılığı artırır.

Bu nedenle modern Spring Boot projelerinde Field Injection yerine Constructor Injection tercih edilmektedir.

Autowired

@Autowired, Spring'in bir Bean'i otomatik olarak bulup ilgili sınıfa enjekte etmesini sağlayan anotasyondur.

Örneğin aşağıdaki kullanımda Spring, UserRepository Bean'ini otomatik olarak bulur ve UserService içerisine aktarır.

@Service
public class UserService {,
@Autowired
    private UserRepository repository;
}

Spring Boot’un son sürümlerinde ise Constructor Injection kullanıldığında @Autowired yazılmasına gerek yoktur.

@Service
@RequiredArgsConstructor
public class UserService {
  private final UserRepository repository;
}

Spring tek constructor bulunduğunu gördüğü için bağımlılığı otomatik olarak enjekte eder.

Bu nedenle günümüzde en yaygın kullanım şekli budur.

Qualifier

Bazı durumlarda aynı türden birden fazla Bean bulunabilir.

Örneğin iki farklı bildirim servisi olduğunu düşünelim.

@Service
public class EmailService implements NotificationService {
}
@Service
public class SmsService implements NotificationService {
}

Bu durumda Spring hangi Bean’in kullanılacağını bilemez ve hata oluşturur.

İşte bu noktada @Qualifier kullanılır.

@Service
@RequiredArgsConstructor
public class UserService {
    @Qualifier("emailService")
    private final NotificationService notificationService;
}

Bu kullanım sayesinde Spring, hangi Bean’in enjekte edilmesi gerektiğini açık şekilde öğrenmiş olur.

Primary

Birden fazla Bean bulunduğunda varsayılan olarak kullanılacak Bean’i belirlemek için @Primary anotasyonu kullanılır.

@Service
@Primary
public class EmailService implements NotificationService {
}

Artık NotificationService istendiğinde Spring öncelikli olarak EmailService Bean'ini kullanacaktır.

Eğer belirli bir yerde farklı Bean kullanılmak istenirse @Qualifier ile bu seçim değiştirilebilir.

Dependency Injection Akışı

Spring Boot içerisinde Dependency Injection mekanizmasının çalışma mantığı aşağıdaki diyagramda özetlenmiştir.

Application Başlıyor
        │
        ▼
Bean'ler Oluşturuluyor
        │
        ▼
Controller Bean'i
        │
        ▼
Service Bean'i Aranıyor
        │
        ▼
Repository Bean'i Aranıyor
        │
        ▼
Bağımlılıklar Enjekte Ediliyor
        │
        ▼
Uygulama Çalışmaya Hazır

Bu süreç tamamen Spring Container tarafından yönetilir ve geliştiricinin manuel olarak nesne oluşturmasına gerek kalmaz.

Spring Boot Katmanlı Mimari (Layered Architecture)

Gerçek hayattaki Spring Boot projeleri yalnızca birkaç Java sınıfından oluşmaz. Uygulama büyüdükçe kodun okunabilirliğini korumak, bakımını kolaylaştırmak ve geliştirme sürecini daha yönetilebilir hale getirmek için belirli bir mimari yapıya ihtiyaç duyulur. Bu noktada en yaygın kullanılan yaklaşım Katmanlı Mimari (Layered Architecture)’dir.

Katmanlı mimari, uygulamanın farklı sorumluluklarını birbirinden ayırarak her katmanın yalnızca kendi görevini yerine getirmesini sağlar. Böylece kod tekrarının önüne geçilir, bağımlılıklar azalır ve uygulama daha modüler bir yapıya kavuşur.

Aşağıdaki diyagram, tipik bir Spring Boot uygulamasındaki veri akışını göstermektedir.

HTTP Request
                        │
                        ▼
                 Controller Layer
                        │
                        ▼
                  Service Layer
                        │
                        ▼
                Repository Layer
                        │
                        ▼
                    Database
                        ▲
                        │
                  Repository Layer
                        ▲
                        │
                  Service Layer
                        ▲
                        │
                 Controller Layer
                        ▲
                        │
                 HTTP Response (JSON)

Bu akışta her katman yalnızca kendisinden sorumlu işlemleri gerçekleştirir ve doğrudan diğer katmanların görevlerine müdahale etmez.

Controller Katmanı

Controller katmanı, istemciden (Client) gelen HTTP isteklerini karşılayan ilk katmandır.

Kullanıcı bir endpoint’e istek gönderdiğinde ilk olarak Controller çalışır. Controller’ın temel görevi gelen isteği almak, gerekli doğrulamaları yapmak ve ilgili Service katmanına yönlendirmektir.

Örneğin aşağıdaki istek gönderildiğinde:

GET /api/users

Bu istek doğrudan ilgili Controller metoduna ulaşır.

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {private final UserService userService;
    @GetMapping
    public List<UserResponse> getAllUsers() {
        return userService.getAllUsers();
    }
}

Controller içerisinde iş kuralları yazılmamalıdır. Bu katman yalnızca istek ve cevap yönetiminden sorumludur.

Controller’ın başlıca görevleri:

  • HTTP isteklerini karşılamak
  • Request verilerini almak
  • DTO kullanarak veri taşımak
  • Service katmanını çağırmak
  • Response döndürmek
  • HTTP Status kodlarını yönetmek

Service Katmanı

Service katmanı, uygulamanın iş kurallarının bulunduğu katmandır.

Bir uygulamadaki hesaplamalar, doğrulamalar, yetkilendirme kontrolleri veya birden fazla Repository işleminin birlikte yürütülmesi gibi işlemler bu katmanda gerçekleştirilir.

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
    public List<UserResponse> getAllUsers() {
        return repository.findAll()
                .stream()
                .map(UserMapper::toResponse)
                .toList();
    }
}

Service katmanı doğrudan HTTP istekleriyle ilgilenmez. Aynı şekilde veritabanı sorgularını da yazmaz. Bu işlemler Repository katmanına bırakılır.

Service katmanının temel görevleri:

  • İş kurallarını uygulamak
  • Repository katmanını yönetmek
  • DTO dönüşümleri yapmak
  • Transaction işlemlerini yürütmek
  • Gerekirse birden fazla Repository kullanmak

Repository Katmanı

Repository katmanı, uygulamanın veritabanı ile iletişim kurduğu katmandır.

Spring Data JPA sayesinde çoğu CRUD işlemi yalnızca birkaç satır kod ile gerçekleştirilebilir.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

Bu yapı sayesinde;

  • Kullanıcı ekleme
  • Güncelleme
  • Silme
  • Listeleme
  • ID ile arama

gibi işlemler otomatik olarak sağlanır.

Repository katmanı yalnızca veri erişiminden sorumludur. İş kuralları burada yazılmamalıdır.

Entity Katmanı

Entity sınıfları, veritabanındaki tabloların Java tarafındaki karşılığıdır.

Her Entity genellikle bir tabloyu temsil eder.

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String email;
}

Spring Boot uygulaması çalışırken JPA bu Entity sınıflarını okuyarak veritabanı tablolarıyla ilişkilendirir.

Entity sınıfları doğrudan API üzerinden istemciye gönderilmemelidir. Bunun yerine DTO kullanılması önerilir.

DTO (Data Transfer Object)

DTO, katmanlar arasında veri taşımak amacıyla kullanılan özel sınıflardır.

Spring Boot projelerinde genellikle iki farklı DTO kullanılır.

  • Request DTO
  • Response DTO

Örneğin kullanıcı oluşturma isteği aşağıdaki gibi olabilir.

public class CreateUserRequest {
    private String username;
    private String email;
}

API cevabı ise farklı bir DTO ile döndürülebilir.

public class UserResponse {
    private Long id;
    private String username;
    private String email;
}

Bu yaklaşım sayesinde Entity sınıfları dış dünyadan izole edilir ve yalnızca gerekli bilgiler paylaşılır.

Config Katmanı

Config klasörü, uygulamaya ait yapılandırma sınıflarını içerir.

Bu katmanda;

  • Security yapılandırmaları
  • CORS ayarları
  • Swagger yapılandırması
  • Bean tanımlamaları
  • ModelMapper
  • ObjectMapper

gibi uygulama genelinde kullanılan ayarlar bulunur.

Örneğin manuel Bean tanımlamak için aşağıdaki yapı kullanılabilir.

@Configuration
public class AppConfig {
@Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
}

Config katmanı sayesinde uygulamanın yapılandırmaları tek bir noktadan yönetilebilir.

Exception Katmanı

Gerçek projelerde oluşabilecek hataların tek tek Controller içerisinde yönetilmesi doğru bir yaklaşım değildir.

Spring Boot projelerinde bunun yerine merkezi bir hata yönetimi uygulanır.

@RestControllerAdvice
public class GlobalExceptionHandler {
}

Bu yapı sayesinde;

  • Kullanıcı bulunamadı
  • Yetkisiz erişim
  • Doğrulama hataları
  • Veritabanı hataları

gibi tüm istisnalar tek bir merkezden yönetilebilir.

Bu yaklaşım kod tekrarını azaltırken hata mesajlarının daha tutarlı olmasını da sağlar.

Katmanlar Birlikte Nasıl Çalışır?

Bir kullanıcının bilgilerini listelemek istediğini düşünelim.

İstek aşağıdaki sırayla ilerler.

Client
   │
   ▼
UserController
   │
   ▼
UserService
   │
   ▼
UserRepository
   │
   ▼
Database
   │
   ▼
UserRepository
   │
   ▼
UserService
   │
   ▼
UserResponse DTO
   │
   ▼
UserController
   │
   ▼
JSON Response

Bu yapı sayesinde her katman yalnızca kendi sorumluluğunu yerine getirir.

  • Controller → HTTP iletişimini yönetir.
  • Service → İş kurallarını uygular.
  • Repository → Veritabanına erişir.
  • Entity → Veriyi temsil eder.
  • DTO → Veriyi güvenli şekilde taşır.
  • Config → Uygulama yapılandırmalarını yönetir.
  • Exception → Hataları merkezi olarak yönetir.

REST API Geliştirme

Modern web ve mobil uygulamalarının büyük çoğunluğu, istemci (client) ile sunucu (server) arasındaki veri iletişimini REST API üzerinden gerçekleştirir. Mobil uygulamalar, web arayüzleri veya üçüncü taraf servisler ihtiyaç duydukları verileri doğrudan veritabanından almak yerine backend üzerinde çalışan REST API’lere istek gönderir.

Spring Boot, Spring MVC altyapısı sayesinde REST API geliştirmeyi oldukça kolaylaştırır. Birkaç anotasyon kullanarak HTTP isteklerini karşılayabilir, verileri JSON formatında döndürebilir ve istemcilerle güvenli bir şekilde haberleşebilirsiniz.

Bir REST API’de veri akışı genel olarak aşağıdaki şekilde gerçekleşir.

HTTP Request
                 │
                 ▼
         Spring Boot Controller
                 │
                 ▼
             Service Layer
                 │
                 ▼
          Repository Layer
                 │
                 ▼
             Database
                 │
                 ▼
           JSON Response

Bu yapıda Controller katmanı gelen HTTP isteklerini karşılar, gerekli işlemleri Service katmanına yönlendirir ve sonucu istemciye JSON formatında döndürür.

@RequestMapping

@RequestMapping, Controller sınıfının veya metodunun hangi URL üzerinden erişileceğini belirleyen temel anotasyondur.

Genellikle Controller seviyesinde ortak URL tanımlamak için kullanılır.

@RestController
@RequestMapping("/api/users")
public class UserController {
}

Bu tanımlama yapıldıktan sonra Controller içerisindeki tüm endpoint’ler /api/users adresi altında çalışacaktır.

Örneğin;

@GetMapping
public List<UserResponse> getAllUsers() {
    ...
}

endpoint’inin tam adresi şu şekilde olur:

GET /api/users

@GetMapping

@GetMapping, sunucudan veri almak için kullanılan HTTP GET isteklerini karşılar.

Veri listeleme veya belirli bir kaydı görüntüleme işlemlerinde kullanılır.

@GetMapping
public List<UserResponse> getUsers() {
return userService.getUsers();
}

Belirli bir kullanıcıyı getirmek için ise:

@GetMapping("/{id}")
public UserResponse getUser(@PathVariable Long id) {
return userService.getUser(id);
}

İstek örneği:

GET /api/users/5

Bu istek sonucunda istemciye JSON formatında kullanıcı bilgileri döndürülür.

@PostMapping

@PostMapping, sisteme yeni bir kayıt eklemek için kullanılır.

İstemciden gelen veriler genellikle @RequestBody ile alınır.

@PostMapping
public UserResponse createUser(
        @RequestBody CreateUserRequest request) {
return userService.create(request);
}

İstek örneği:

POST /api/users
{
  "username": "gazihan",
  "email": "gazihan@example.com"
}

Başarılı bir işlem sonucunda sunucu yeni oluşturulan kullanıcıyı veya oluşturma sonucunu istemciye döndürür.

@PutMapping

@PutMapping, mevcut bir kaydın tamamını güncellemek için kullanılır.

PUT isteğinde gönderilmeyen alanlar genellikle eski değerlerini korumaz. Bu nedenle tüm nesnenin gönderilmesi beklenir.

@PutMapping("/{id}")
public UserResponse updateUser(
        @PathVariable Long id,
        @RequestBody UpdateUserRequest request) {
return userService.update(id, request);
}

Örnek istek:

PUT /api/users/5
{
  "username": "gazihan",
  "email": "newmail@example.com"
}

@PatchMapping

@PatchMapping, bir kaydın yalnızca belirli alanlarını güncellemek için kullanılır.

Örneğin sadece kullanıcının e-posta adresi değiştirilecekse tüm nesnenin gönderilmesine gerek yoktur.

@PatchMapping("/{id}")
public UserResponse updateEmail(
        @PathVariable Long id,
        @RequestBody UpdateEmailRequest request) {
return userService.updateEmail(id, request);
}

Örnek istek:

PATCH /api/users/5
{
  "email": "newmail@example.com"
}

Bu yaklaşım hem daha az veri gönderilmesini sağlar hem de gereksiz güncellemelerin önüne geçer.

@DeleteMapping

@DeleteMapping, mevcut bir kaydı silmek için kullanılır.

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
} 

İstek örneği:

DELETE /api/users/5

Başarılı bir silme işleminden sonra genellikle 204 No Content veya 200 OK durum kodu döndürülür.

HTTP Metotlarının Karşılaştırılması

REST API geliştirirken her HTTP metodunun farklı bir amacı vardır.

HTTP MetoduAmaçGETVeri getirmePOSTYeni kayıt oluşturmaPUTKaydı tamamen güncellemePATCHKaydın belirli alanlarını güncellemeDELETEKaydı silme

Bu metotların doğru kullanılması REST mimarisinin temel prensiplerinden biridir.

ResponseEntity

Spring Boot’ta Controller metodundan yalnızca veri döndürmek yeterli olmayabilir. Bazı durumlarda HTTP durum kodunu (Status Code), Header bilgilerini veya özel cevapları da yönetmek gerekir.

Bu amaçla ResponseEntity sınıfı kullanılır.

Örneğin başarılı bir listeleme işlemi:

@GetMapping
public ResponseEntity<List<UserResponse>> getUsers() {
return ResponseEntity.ok(userService.getUsers());
}

Yeni kayıt oluşturulduğunda ise daha doğru yaklaşım 201 Created durum kodu döndürmektir.

@PostMapping
public ResponseEntity<UserResponse> createUser(
        @RequestBody CreateUserRequest request) {
UserResponse response = userService.create(request);
    return ResponseEntity.status(HttpStatus.CREATED)
            .body(response);
}

Kayıt bulunamadığında ise uygun hata kodu döndürülebilir.

return ResponseEntity.notFound().build();

Sık kullanılan ResponseEntity metotları:

Sık Kullanılan ResponseEntity Metotları

REST API geliştirirken en sık kullanılan ResponseEntity metotları ve karşılık geldikleri HTTP durum kodları şunlardır:

  • **ResponseEntity.ok()**
  • HTTP Status: 200 OK
  • İstek başarıyla tamamlandığında ve istemciye veri döndürülmek istendiğinde kullanılır.
  • **ResponseEntity.status(HttpStatus.CREATED)**
  • HTTP Status: 201 Created
  • Yeni bir kaynak başarıyla oluşturulduğunda, genellikle POST isteklerinden sonra kullanılır.
  • **ResponseEntity.noContent()**
  • HTTP Status: 204 No Content
  • İşlem başarılıdır ancak istemciye döndürülecek herhangi bir içerik bulunmamaktadır. Genellikle DELETE işlemlerinde tercih edilir.
  • **ResponseEntity.badRequest()**
  • HTTP Status: 400 Bad Request
  • İstemciden gönderilen isteğin eksik veya hatalı olduğu durumlarda kullanılır.
  • **ResponseEntity.notFound()**
  • HTTP Status: 404 Not Found
  • İstenen kaynağın sistemde bulunamadığı durumlarda döndürülür.

Bu yapı sayesinde istemci yalnızca veriyi değil, işlemin başarılı olup olmadığını da HTTP durum kodları üzerinden anlayabilir.

REST API İstek Akışı

Bir kullanıcının sisteme yeni kayıt oluşturduğunu düşünelim.

Client
   │
   ▼
POST /api/users
   │
   ▼
UserController
   │
   ▼
UserService
   │
   ▼
UserRepository
   │
   ▼
Database
   │
   ▼
UserResponse
   │
   ▼
ResponseEntity
   │
   ▼
HTTP 201 Created

Bu akış, Spring Boot’ta REST API geliştirme sürecinin temelini oluşturur.

DTO (Data Transfer Object) Kullanımı

Spring Boot projelerinde en sık yapılan hatalardan biri, veritabanını temsil eden Entity sınıflarını doğrudan API üzerinden istemciye göndermektir. Küçük projelerde bu yöntem çalışıyor gibi görünse de uygulama büyüdükçe güvenlik, performans ve bakım açısından ciddi sorunlara neden olabilir.

Bu nedenle profesyonel Spring Boot projelerinde katmanlar arasında veri taşımak için DTO (Data Transfer Object) sınıfları kullanılır.

DTO, yalnızca gerekli verileri taşımak amacıyla oluşturulan ve iş mantığı içermeyen Java sınıflarıdır. Böylece istemciye sadece ihtiyaç duyulan bilgiler gönderilir ve uygulamanın iç yapısı dış dünyadan gizlenmiş olur.

Request DTO

Request DTO, istemciden (Client) sunucuya gelen verileri karşılamak için kullanılır.

Örneğin bir kullanıcı oluşturulurken yalnızca gerekli alanların alınması yeterlidir.

public class CreateUserRequest {
private String username;
    private String email;
    private String password;
}

Controller içerisinde ise gelen istek doğrudan Request DTO ile alınabilir.

@PostMapping
public ResponseEntity<UserResponse> createUser(
        @RequestBody CreateUserRequest request) {
return ResponseEntity.ok(userService.create(request));
}

Bu yaklaşım sayesinde istemcinin yalnızca izin verilen alanları göndermesi sağlanır.

Response DTO

Response DTO ise sunucudan istemciye gönderilecek verileri temsil eder.

Her zaman veritabanındaki tüm bilgileri kullanıcıya göndermek doğru değildir. Örneğin kullanıcı şifresi veya sistem içi alanlar API cevabında yer almamalıdır.

public class UserResponse {
private Long id;
    private String username;
    private String email;
}

Controller yalnızca bu DTO’yu döndürür.

@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUser(id));
}

Böylece istemci yalnızca ihtiyacı olan verilere erişebilir.

Mapping

DTO ile Entity birbirinden farklı sınıflar olduğu için verilerin birbirine dönüştürülmesi gerekir. Bu işleme Mapping adı verilir.

Örneğin Entity’den Response DTO’ya dönüşüm şu şekilde yapılabilir.

public class UserMapper {
public static UserResponse toResponse(User user) {
        UserResponse response = new UserResponse();
        response.setId(user.getId());
        response.setUsername(user.getUsername());
        response.setEmail(user.getEmail());
        return response;
    }
}

Benzer şekilde Request DTO’dan Entity oluşturulabilir.

public static User toEntity(CreateUserRequest request) {
User user = new User();
    user.setUsername(request.getUsername());
    user.setEmail(request.getEmail());
    user.setPassword(request.getPassword());
    return user;
}

Büyük projelerde bu dönüşümler manuel olarak yapılabileceği gibi MapStruct veya ModelMapper gibi kütüphaneler de kullanılabilir.

Neden Entity Döndürmüyoruz?

Entity sınıfları veritabanını temsil eder ve çoğu zaman API üzerinden paylaşılmaması gereken alanlar içerir.

Örneğin aşağıdaki Entity sınıfını inceleyelim.

@Entity
public class User {
private Long id;
    private String username;
    private String email;
    private String password;
    private LocalDateTime createdAt;
}

Bu Entity doğrudan API’den döndürülürse istemci aşağıdaki gibi bir cevap alabilir.

{
  "id": 1,
  "username": "gazihan",
  "email": "gazihan@example.com",
  "password": "123456",
  "createdAt": "2026-07-22T15:30:00"
}

Görüldüğü gibi kullanıcı şifresi gibi paylaşılmaması gereken bilgiler de istemciye gönderilmiş olur. Bu durum ciddi bir güvenlik problemidir.

Bunun yerine Response DTO kullanıldığında yalnızca gerekli alanlar döndürülür.

{
  "id": 1,
  "username": "gazihan",
  "email": "gazihan@example.com"
}

Entity yerine DTO kullanmanın başlıca avantajları şunlardır:

  • Hassas verilerin istemciye gönderilmesini engeller.
  • API ile veritabanı modelini birbirinden bağımsız hale getirir.
  • Gereksiz alanların gönderilmesini önleyerek performansı artırır.
  • Entity üzerinde yapılan değişikliklerin API’yi doğrudan etkilemesini engeller.
  • Daha okunabilir ve sürdürülebilir bir mimari oluşturur.

Veri Akışı

DTO kullanımındaki veri akışı aşağıdaki şekilde özetlenebilir.

Client
                   │
                   ▼
          Request DTO
                   │
                   ▼
            Controller
                   │
                   ▼
              Service
                   │
                   ▼
          Request → Entity
                   │
                   ▼
             Repository
                   │
                   ▼
             Database
                   │
                   ▼
               Entity
                   │
                   ▼
         Entity → Response DTO
                   │
                   ▼
            JSON Response

Bu yapı sayesinde istemci hiçbir zaman doğrudan Entity sınıflarıyla iletişim kurmaz. Tüm veri alışverişi DTO’lar üzerinden gerçekleştirilir.

Validation (Veri Doğrulama)

Bir REST API geliştirirken istemciden gelen verilerin her zaman doğru ve eksiksiz olacağını varsayamayız. Kullanıcı boş alan gönderebilir, geçersiz bir e-posta adresi yazabilir veya beklenen uzunluğun dışında veriler iletebilir. Bu tür hataların uygulamanın iş mantığına ulaşmadan önce kontrol edilmesi gerekir.

Spring Boot, Bean Validation (Jakarta Validation) desteği sayesinde gelen verilerin belirlenen kurallara uygun olup olmadığını otomatik olarak doğrulayabilir. Böylece gereksiz if-else kontrolleri yazmadan daha temiz ve okunabilir bir kod yapısı elde edilir.

Validation işlemleri genellikle DTO sınıfları üzerinde uygulanır.

@Valid

@Valid, bir DTO nesnesinin içerisindeki doğrulama kurallarını çalıştırmak için kullanılır.

Örneğin aşağıdaki DTO’da bazı doğrulama kuralları tanımlanmış olsun.

public class CreateUserRequest {
@NotBlank
    private String username;
    @Email
    private String email;
}

Controller içerisinde @Valid kullanıldığında Spring, istek gelmeden önce bu kuralları otomatik olarak kontrol eder.

@PostMapping
public ResponseEntity<UserResponse> createUser(
        @Valid @RequestBody CreateUserRequest request) {
return ResponseEntity.ok(userService.create(request));
}

Eğer gönderilen veriler kurallara uymuyorsa ilgili Controller metodu çalıştırılmaz ve Spring otomatik olarak doğrulama hatası döndürür.

@Validated

@Validated, @Valid ile benzer amaca hizmet eder ancak daha gelişmiş senaryolar için kullanılır.

Özellikle Service katmanında doğrulama yapmak veya Validation Group kullanmak gerektiğinde tercih edilir.

@Service
@Validated
public class UserService {
}

Günlük Spring Boot projelerinde Controller seviyesinde çoğunlukla @Valid, daha özel ihtiyaçlarda ise @Validated kullanılır.

@NotNull

@NotNull, ilgili alanın null olmamasını sağlar.

public class ProductRequest {
@NotNull
    private Long categoryId;
}

Bu anotasyon yalnızca null değerini engeller.

Aşağıdaki değerler geçerlidir:

  • 0
  • "" (boş String)
  • " " (sadece boşluk)

Yani yalnızca nesnenin null olmaması kontrol edilir.

@NotBlank

@NotBlank, String alanlar için en sık kullanılan doğrulama anotasyonlarından biridir.

public class CreateUserRequest {
@NotBlank
    private String username;
}

Bu anotasyon;

  • null
  • ""
  • " " (yalnızca boşluk)

değerlerinin tamamını geçersiz kabul eder.

Kullanıcı adı, ad-soyad veya başlık gibi metinsel alanlarda genellikle @NotBlank tercih edilir.

@Size

@Size, String, List veya Collection türündeki alanların minimum ve maksimum uzunluğunu belirlemek için kullanılır.

public class CreateUserRequest {
@Size(min = 3, max = 20)
    private String username;
}

Yukarıdaki örnekte kullanıcı adı en az 3, en fazla 20 karakter olabilir.

Bu anotasyon özellikle;

  • kullanıcı adı
  • şifre
  • açıklama
  • liste boyutu

gibi alanlarda sıkça kullanılır.

@Email

@Email, gönderilen değerin geçerli bir e-posta formatında olup olmadığını kontrol eder.

public class CreateUserRequest {
@Email
    private String email;
}

Örneğin;

Geçerli:

gazihan@example.com

Geçersiz:

gazihanexample.com
gazihan@
@example.com

Bu sayede istemciden gelen hatalı e-posta adresleri uygulama içerisine ulaşmadan engellenmiş olur.

Validation Örneği

Bir DTO üzerinde birden fazla doğrulama kuralı birlikte kullanılabilir.

public class RegisterRequest {
@NotBlank
    @Size(min = 3, max = 30)
    private String username;
    @Email
    @NotBlank
    private String email;
    @NotBlank
    @Size(min = 8, max = 50)
    private String password;
}

Bu yapı sayesinde Spring Boot tek bir istek içerisinde tüm doğrulamaları otomatik olarak gerçekleştirir.

Validation Akışı

Validation süreci aşağıdaki şekilde çalışır.

Client
   │
   ▼
HTTP Request
   │
   ▼
@Valid
   │
   ▼
Validation Kuralları
   │
   ├───────────────► Başarısız
   │                     │
   │                     ▼
   │              400 Bad Request
   │
   ▼
Controller
   │
   ▼
Service
   │
   ▼
Database

Eğer doğrulama başarısız olursa istek iş katmanına ulaşmadan sonlandırılır ve istemciye uygun bir hata mesajı döndürülür.

Exception Handling (Hata Yönetimi)

Gerçek hayattaki uygulamalarda her isteğin başarılı sonuçlanacağını düşünmek doğru değildir. Kullanıcı olmayan bir kaydı isteyebilir, hatalı veri gönderebilir veya sistemde beklenmeyen bir hata oluşabilir. Bu tür durumların doğru şekilde yönetilmesi hem kullanıcı deneyimi hem de uygulamanın güvenilirliği açısından büyük önem taşır.

Spring Boot, hata yönetimini merkezi hale getirmek için güçlü mekanizmalar sunar. Böylece her Controller içerisinde aynı try-catch bloklarını yazmak yerine, tüm hatalar tek bir noktadan yönetilebilir.

@ControllerAdvice

@ControllerAdvice, uygulamadaki tüm Controller sınıflarını kapsayan merkezi bir hata yönetim mekanizmasıdır.

Bu anotasyon sayesinde oluşan istisnalar tek bir sınıfta yakalanabilir ve istemciye standart bir hata cevabı döndürülebilir.

@RestControllerAdvice
public class GlobalExceptionHandler {
}

*@RestControllerAdvice, @ControllerAdvice ve @ResponseBody anotasyonlarının birleşimidir. REST API geliştirilirken genellikle bu yapı tercih edilir.*

Bu yaklaşım sayesinde hata yönetimi tek bir merkezden gerçekleştirilir ve kod tekrarının önüne geçilir.

@ExceptionHandler

@ExceptionHandler, belirli bir Exception türünü yakalamak için kullanılır.

Örneğin kullanıcı bulunamadığında oluşan UserNotFoundException aşağıdaki gibi yakalanabilir.

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(
            UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ex.getMessage());
    }
}

Bu yapı sayesinde UserNotFoundException oluştuğunda uygulama hata vererek durmak yerine istemciye anlamlı bir cevap döndürür.

Custom Exception

Spring Boot içerisinde yalnızca hazır Exception sınıflarını kullanmak zorunda değilsiniz. İhtiyacınıza göre kendi özel Exception sınıflarınızı da oluşturabilirsiniz.

Örneğin sistemde bulunmayan bir kullanıcı için aşağıdaki gibi bir Exception yazılabilir.

public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
        super(message);
    }
}

Service katmanında kullanıcı bulunamadığında bu Exception fırlatılabilir.

public User findById(Long id) {
return repository.findById(id)
            .orElseThrow(() ->
                    new UserNotFoundException("Kullanıcı bulunamadı."));
}

Bu yaklaşım sayesinde hata mesajları daha anlaşılır hale gelir ve her hata senaryosu için özel çözümler geliştirilebilir.

Global Exception Handling

Bir uygulamada yalnızca tek bir hata türü bulunmaz. Doğrulama hataları, veritabanı hataları, yetkilendirme problemleri veya beklenmeyen sistem hataları oluşabilir.

Bu nedenle profesyonel projelerde tüm istisnalar tek bir sınıfta toplanır.

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(
            UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ex.getMessage());
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(
            Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Beklenmeyen bir hata oluştu.");
    }
}

Bu yapı sayesinde uygulamanın tamamında oluşan hatalar tek bir merkezden yönetilebilir.

Exception Akışı

Spring Boot’ta bir Exception oluştuğunda süreç genel olarak aşağıdaki şekilde ilerler.

Client
   │
   ▼
Controller
   │
   ▼
Service
   │
   ▼
Exception Oluştu
   │
   ▼
@ControllerAdvice
   │
   ▼
@ExceptionHandler
   │
   ▼
ResponseEntity
   │
   ▼
HTTP Error Response

Örneğin istemci olmayan bir kullanıcıyı istediğinde aşağıdaki cevap döndürülebilir.

HTTP/1.1 404 Not Found
{
    "message": "Kullanıcı bulunamadı."
}

Bu sayede istemci hem doğru HTTP durum kodunu hem de hatanın nedenini kolayca anlayabilir.

Neden Global Exception Kullanıyoruz?

Merkezi hata yönetimi birçok avantaj sağlar.

  • Kod tekrarını azaltır.
  • Tüm API’lerde tutarlı hata cevapları oluşturur.
  • Controller sınıflarını sadeleştirir.
  • Hata yönetimini tek noktadan kontrol etmeyi sağlar.
  • Bakımı ve geliştirilmesi daha kolay bir yapı sunar.

Profesyonel Spring Boot projelerinde neredeyse her zaman Global Exception Handler kullanılır.

Spring Boot Security

Günümüzde geliştirilen web ve mobil uygulamalarında güvenlik, yalnızca kullanıcı adı ve şifre kontrolünden ibaret değildir. Kullanıcının kimliğinin doğrulanması, hangi işlemleri yapabileceğinin belirlenmesi ve uygulamanın yetkisiz erişimlere karşı korunması güvenli bir backend’in temel gereksinimlerindendir.

Spring Boot, bu ihtiyaçları karşılamak için Spring Security adlı güçlü bir güvenlik çatısını sunar. Spring Security; kimlik doğrulama (Authentication), yetkilendirme (Authorization), oturum yönetimi, şifreleme ve saldırılara karşı koruma gibi birçok özelliği hazır olarak sağlar.

Spring Security Entegrasyonu

Spring Security kullanabilmek için öncelikle projeye gerekli bağımlılık eklenir.

Maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Bağımlılık eklendikten sonra uygulama yeniden çalıştırıldığında Spring Security otomatik olarak devreye girer.

Varsayılan olarak;

  • Tüm endpoint’ler korunur.
  • Kimlik doğrulaması yapılmadan isteklere izin verilmez.
  • Spring geçici bir kullanıcı oluşturur.
  • Konsola rastgele oluşturulmuş bir parola yazdırılır.

Bu davranışlar tamamen özelleştirilebilir ve uygulamanın ihtiyaçlarına göre yapılandırılabilir.

Authentication (Kimlik Doğrulama)

Authentication, sisteme erişmeye çalışan kişinin gerçekten iddia ettiği kullanıcı olup olmadığını doğrulama işlemidir.

Örneğin kullanıcı giriş ekranında kullanıcı adı ve şifresini gönderir.

Email: gazihan@example.com
Password: ********

Spring Security bu bilgileri veritabanındaki kayıtlarla karşılaştırır.

  • Bilgiler doğruysa kullanıcı doğrulanır.
  • Bilgiler yanlışsa giriş reddedilir.

Kısacası Authentication şu sorunun cevabını verir:

“Sen gerçekten kimsin?”

Authorization (Yetkilendirme)

Authentication başarılı olduktan sonra kullanıcı her işlemi yapamayabilir.

Örneğin;

  • Normal kullanıcı yalnızca kendi profilini görüntüleyebilir.
  • Yönetici kullanıcı sistemdeki tüm kullanıcıları yönetebilir.

Bu kontrol Authorization olarak adlandırılır.

Örneğin yalnızca ADMIN rolüne sahip kullanıcıların erişebileceği bir endpoint aşağıdaki gibi tanımlanabilir.

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
}

Authorization şu sorunun cevabını verir:

“Bu işlemi yapmaya yetkin var mı?”

Authentication ve Authorization Arasındaki Fark

Bu iki kavram sıklıkla karıştırılır.

AuthenticationAuthorizationKullanıcının kimliğini doğrular.Kullanıcının hangi işlemleri yapabileceğini belirler.Giriş aşamasında gerçekleşir.Giriş yapıldıktan sonra devreye girer.”Kimsin?” sorusuna cevap verir.”Ne yapabilirsin?” sorusuna cevap verir.

Basit bir örnekle;

  • Sisteme giriş yapmak → Authentication
  • Kullanıcı silme yetkisini kontrol etmek → Authorization

Spring Security Çalışma Mantığı

Bir HTTP isteği geldiğinde Spring Security isteği doğrudan Controller’a göndermez.

Önce güvenlik kontrolleri gerçekleştirilir.

Client
   │
   ▼
Spring Security
   │
   ▼
Authentication
   │
   ▼
Authorization
   │
   ▼
Controller
   │
   ▼
Response

Bu sayede yetkisiz kullanıcılar uygulamanın iş katmanına ulaşamaz.

JWT (JSON Web Token) Entegrasyonu

REST API geliştirilirken istemcinin her istekte yeniden kullanıcı adı ve şifre göndermesi hem güvenlik hem de performans açısından doğru bir yaklaşım değildir. Bunun yerine kullanıcı giriş yaptıktan sonra kendisine bir JWT (JSON Web Token) verilir ve sonraki isteklerde kimliğini bu token ile doğrular.

JWT, kullanıcı bilgilerini güvenli bir şekilde taşıyan ve dijital olarak imzalanmış metin tabanlı bir belirteçtir. Sunucu, gelen token’ın geçerliliğini kontrol ederek kullanıcının tekrar giriş yapmasına gerek kalmadan istekleri doğrulayabilir.

Access Token

Access Token, kullanıcının sisteme erişebilmesini sağlayan kısa ömürlü JWT’dir.

Başarılı bir giriş işleminden sonra sunucu kullanıcıya bir Access Token üretir.

POST /login
        │
        ▼
Kullanıcı doğrulandı
        │
        ▼
Access Token oluşturuldu
        │
        ▼
İstemciye gönderildi

İstemci sonraki tüm isteklerde bu token’ı HTTP Header içerisinde gönderir.

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Access Token’lar genellikle güvenlik amacıyla kısa süreli (örneğin 15–30 dakika) oluşturulur.

Refresh Token

Access Token’ın süresi dolduğunda kullanıcıyı tekrar giriş ekranına yönlendirmek kullanıcı deneyimini olumsuz etkiler.

Bu problemi çözmek için Refresh Token kullanılır.

Refresh Token’ın temel görevi yeni bir Access Token üretmektir.

Login
   │
   ▼
Access Token
Refresh Token

Süreç şu şekilde işler:

  1. Kullanıcı giriş yapar.
  2. Sunucu access Token ve Refresh Token üretir.
  3. Access Token süresi dolduğunda istemci Refresh Token’ı gönderir.
  4. Sunucu yeni bir Access Token üretir.
  5. Kullanıcı tekrar giriş yapmak zorunda kalmaz.

Refresh Token’lar Access Token’lardan daha uzun ömürlüdür ve güvenli bir şekilde saklanmalıdır.

JWT Filter

Her HTTP isteğinde kullanıcının token bilgisini kontrol etmek gerekir.

Bu işlem Controller içerisinde yapılmaz. Bunun yerine Spring Security filtreleri kullanılır.

JWT Filter’ın temel görevi:

  • Authorization Header’ını okumak
  • Bearer token’ı almak
  • Token’ın geçerli olup olmadığını kontrol etmek
  • Kullanıcı bilgilerini Security Context’e eklemek

İstek akışı aşağıdaki gibidir.

HTTP Request
      │
      ▼
JWT Filter
      │
      ▼
Token Okundu
      │
      ▼
Geçerli mi?
   │          │
  Evet       Hayır
   │          │
   ▼          ▼
Controller   401 Unauthorized

Bu sayede geçersiz veya eksik token’a sahip kullanıcılar uygulamaya erişemez.

Token Doğrulama

JWT kullanmanın en önemli adımı token’ın doğrulanmasıdır.

Spring Security, gelen token üzerinde aşağıdaki kontrolleri gerçekleştirir.

  • Token’ın imzası doğru mu?
  • Token değiştirilmiş mi?
  • Süresi dolmuş mu?
  • Beklenen kullanıcıya ait mi?
  • Geçerli formatta mı?

Bu kontrollerden herhangi biri başarısız olursa istek reddedilir ve istemciye genellikle aşağıdaki HTTP durum kodu döndürülür.

401 Unauthorized

Geçerli bir token ise kullanıcı bilgileri Security Context’e eklenir ve istek Controller katmanına ulaşır.

JWT Akışı

JWT ile kimlik doğrulama süreci aşağıdaki şekilde gerçekleşir.

Login
             │
             ▼
Authentication
             │
             ▼
 Access Token + Refresh Token
             │
             ▼
        Client
             │
             ▼
Authorization: Bearer Token
             │
             ▼
        JWT Filter
             │
             ▼
     Token Doğrulama
             │
      ┌──────┴──────┐
      │             │
   Geçerli      Geçersiz
      │             │
      ▼             ▼
 Controller   401 Unauthorized

Swagger / OpenAPI

Bir REST API geliştirilirken yalnızca endpoint’lerin çalışması yeterli değildir. API’yi kullanacak geliştiricilerin hangi endpoint’lerin bulunduğunu, hangi parametreleri aldığını ve nasıl cevap döndürdüğünü de kolayca görebilmesi gerekir. Bu ihtiyacı karşılamak için en yaygın kullanılan araç Swagger (OpenAPI)’dır.

Swagger, Spring Boot uygulamanızdaki tüm REST endpoint’lerini otomatik olarak dokümante eder ve tarayıcı üzerinden test edebilmenizi sağlayan bir arayüz sunar.

Swagger Kurulumu

Spring Boot 3 ve üzeri sürümlerde en yaygın kullanılan kütüphane springdoc-openapi’dir.

Maven

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.8.9</version>
</dependency>

Bağımlılık eklendikten sonra uygulama çalıştırıldığında Swagger arayüzüne aşağıdaki adres üzerinden erişilebilir.

http://localhost:8080/swagger-ui/index.html

OpenAPI dokümanı ise şu adreste oluşturulur.

http://localhost:8080/v3/api-docs

Endpoint Dokümantasyonu

Swagger, Controller içerisindeki endpoint’leri otomatik olarak listeler. İstenirse açıklamalar eklenerek dokümantasyon daha anlaşılır hale getirilebilir.

@RestController
@RequestMapping("/api/users")
public class UserController {
@Operation(summary = "Kullanıcı listesini getirir")
    @GetMapping
    public List<UserResponse> getUsers() {
        return userService.getUsers();
    }
}

Parametreler ve cevaplar da açıklanabilir.

@Operation(summary = "ID'ye göre kullanıcı getirir")
@ApiResponses({
    @ApiResponse(responseCode = "200", description = "Başarılı"),
    @ApiResponse(responseCode = "404", description = "Kullanıcı bulunamadı")
})
@GetMapping("/{id}")
public UserResponse getUser(@PathVariable Long id) {
return userService.getUser(id);
}

Swagger sayesinde;

  • Tüm endpoint’ler tek ekranda görüntülenebilir.
  • Parametreler ve dönüş tipleri incelenebilir.
  • Tarayıcı üzerinden API testleri yapılabilir.
  • Frontend ve mobil geliştiriciler API’yi kolayca kullanabilir.

Scheduled Görevler

Bazı işlemlerin kullanıcı isteği olmadan belirli zamanlarda otomatik olarak çalıştırılması gerekir. Örneğin günlük rapor oluşturma, önbellek temizleme, e-posta gönderme veya veritabanı yedekleme gibi işlemler belirli aralıklarla çalıştırılabilir.

Spring Boot bu tür işlemler için Scheduling desteği sunar.

@EnableScheduling

Zamanlanmış görevleri kullanabilmek için öncelikle Scheduling özelliği aktif edilmelidir.

@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Bu anotasyon eklendikten sonra Spring, zamanlanmış görevleri otomatik olarak çalıştırmaya başlar.

@Scheduled

Belirli aralıklarla çalışacak metotlar @Scheduled anotasyonu ile işaretlenir.

Örneğin her 10 saniyede bir çalışan görev:

@Component
public class ReportScheduler {
@Scheduled(fixedRate = 10000)
    public void generateReport() {
        System.out.println("Rapor oluşturuldu.");
    }
}

Sık kullanılan kullanım şekilleri:

  • fixedRate → Belirli aralıklarla çalıştırır.
  • fixedDelay → Önceki görev tamamlandıktan sonra bekleyerek çalıştırır.
  • initialDelay → İlk çalışmadan önce bekleme süresi tanımlar.

Cron İfadeleri

Daha esnek zamanlama ihtiyaçları için Cron ifadeleri kullanılır.

Örneğin her gün saat 02:00'de çalışan görev:

@Scheduled(cron = "0 0 2 * * *")
public void backupDatabase() {
}

Bazı yaygın Cron örnekleri:

CronAçıklama0 * * * * *Her dakika0 0 * * * *Her saat0 0 2 * * *Her gün saat 02:000 0 9 * * MONHer pazartesi saat 09:00

Scheduled Görev Akışı

Uygulama Başlıyor
        │
        ▼
@EnableScheduling
        │
        ▼
@Scheduled Metodu
        │
        ▼
Belirlenen Zamanda Çalışır
        │
        ▼
Görev Tamamlanır

Sonuç

Bu yazıda Java ve Spring Boot ile backend geliştirmenin temel yapı taşlarını ele aldık. Proje yapısından REST API geliştirmeye, katmanlı mimariden güvenlik ve test süreçlerine kadar profesyonel bir Spring Boot uygulamasında sıkça kullanılan konuları özetledik.

Spring Boot’u öğrenmenin en etkili yolu ise öğrendiğiniz kavramları gerçek projelerde uygulamaktır. Bol bol pratik yaparak ve farklı senaryolar geliştirerek hem teknik bilginizi hem de problem çözme becerinizi geliştirebilirsiniz.

Umarım bu rehber, Spring Boot ile backend geliştirme yolculuğunuzda faydalı bir kaynak olmuştur. İyi çalışmalar ve keyifli kodlamalar!


메타데이터
post_id
fd3faa596a54
slug
java-ve-spring-boot-ile-modern-backend-geliştirme-fd3faa596a54
url
https://medium.com/@gazihanizci/java-ve-spring-boot-ile-modern-backend-geli%C5%9Ftirme-fd3faa596a54
canonical_url
https://medium.com/@gazihanizci/java-ve-spring-boot-ile-modern-backend-geli%C5%9Ftirme-fd3faa596a54
author_url
https://medium.com/@gazihanizci
status
ok
fetched_at
2026-08-08 09:05:23