- Bean ve konteyner: temel fikir
- Bean nasıl tanımlanır?
- Enjeksiyon biçimleri: yapıcı metot neden tercih edilir?
- Aynı türden birden fazla bean olduğunda
- Kapsam ve yaşam döngüsü
- Döngüsel bağımlılık ve diğer başlangıç hataları
- Sık yapılan hatalar
- Bu beceriyi Mindset programlarında nerede çalışırsınız?
- Sık sorulan sorular
Spring'de bean'ler ve bağımlılık enjeksiyonu: nasıl çalışır, nasıl kullanılır
Spring'de bean oluşturma, yapıcı metotla enjeksiyon, kapsam, yaşam döngüsü ve yaygın bağımlılık hatalarını örneklerle adım adım öğrenin.
Bean ve konteyner: temel fikir
Spring'de uygulamanızdaki nesneleri siz değil bir konteyner oluşturur ve yönetir. Konteynerin yönettiği her nesneye bean denir. Siz bir sınıfın hangi diğer sınıflara ihtiyaç duyduğunu bildirirsiniz; konteyner doğru nesneyi bulup sınıfa verir. Buna bağımlılık enjeksiyonu denir.
Bunun sağladığı en somut fayda, bir sınıfın bağımlılığını kendisinin yaratmamasıdır. Bir sipariş servisi veri erişim nesnesini kendisi oluşturursa, onu test sırasında sahte bir nesneyle değiştirmek zorlaşır. Bağımlılık dışarıdan verilirse, test için sahte nesne vermek ve üretim için gerçeğini vermek aynı sınıfı değiştirmeden mümkün olur. Gevşek bağlı, test edilebilir kod bu fikrin sonucudur.
Bean nasıl tanımlanır?
Yaygın yollar şunlardır.
İlk yol, sınıfı stereotip anotasyonlarla işaretlemek: @Component genel amaçlı, @Service iş mantığı, @Repository veri erişimi, @Controller ve @RestController web katmanı içindir. Spring Boot'un ana sınıfındaki anotasyon, bileşen taramasını başlatır ve bu sınıfları bulur. Tarama, ana sınıfın bulunduğu paket ve altında çalışır; bir sınıf bu paketin dışında kalırsa bean olarak bulunmaz ve 'bean bulunamadı' hatası alırsınız.
Diğer yol, yapılandırma sınıfında @Bean metodu yazmak:
@Configuration class AppConfig { @Bean PaymentClient paymentClient(PaymentSettings settings) { return new PaymentClient(settings.address()); } }
Harici bir kitaplıktan gelen ve üzerine anotasyon koyamayacağınız sınıflar için bu yol gerekir. Metodun parametreleri de konteynerden enjekte edilir; bean'ler birbirine bu şekilde bağlanır.
Enjeksiyon biçimleri: yapıcı metot neden tercih edilir?
Bağımlılığı sınıfa farklı yollarla verebilirsiniz: yapıcı metot üzerinden, set metodu üzerinden ve alan üzerinden (@Autowired ile). Önerilen yol yapıcı metottur:
@Service class OrderService { private final StockService stock; private final PaymentClient payment;
OrderService(StockService stock, PaymentClient payment) { this.stock = stock; this.payment = payment; } }
Bu yapının üstünlükleri somuttur. Alanlar final olabilir, yani nesne oluştuğunda eksiksiz ve değişmezdir. Bağımlılıklar yapıcı imzasında görünür; bir sınıfın çok fazla bağımlılığı varsa bu, sınıfın fazla iş yaptığının erken işaretidir. Test sırasında konteyner olmadan, yapıcıya sahte nesneler verip sınıfı doğrudan oluşturabilirsiniz. Alan enjeksiyonu ise bu avantajların hiçbirini vermez ve sınıfı konteynerden bağımsız test etmeyi zorlaştırır. Tek yapıcısı olan sınıfta @Autowired yazmak gerekmez.
Aynı türden birden fazla bean olduğunda
Konteyner, enjeksiyonu tür üzerinden çözer. Aynı arayüzü gerçekleyen birden fazla bean varsa hangisinin seçileceğini belirtmeniz gerekir; yoksa uygulama başlarken 'tek bean beklenirken birden fazla bulundu' hatasıyla durur.
Çözüm seçenekleri: bean'lerden birini @Primary ile varsayılan işaretlemek; enjeksiyon noktasında @Qualifier ile adıyla seçmek; ya da bütün gerçeklemeleri liste veya harita olarak enjekte edip uygun olanı kendiniz seçmek. Örneğin birden çok bildirim kanalı varsa (e-posta, kısa mesaj) hepsini bir liste olarak alıp kullanıcı tercihine göre birini çağırmak, koşullu yapıyı bir stratejiye çevirir ve yeni kanal eklemek için mevcut kodu değiştirmeyi gerektirmez.
Koşullu bean tanımlamak da mümkündür: yapılandırma değerine ya da profile göre bir bean'in oluşturulup oluşturulmayacağı belirlenebilir. Örneğin test profilinde sahte ödeme istemcisi, üretim profilinde gerçeği etkinleşir.
Kapsam ve yaşam döngüsü
Varsayılan kapsam tekil (singleton) olmasıdır: konteyner her bean'den uygulama boyunca yalnızca bir örnek oluşturur ve herkese aynısını verir. Bu nedenle bean'ler eşzamanlı isteklerce paylaşılır ve içlerinde istek ya da kullanıcıya özgü değişken durum tutmamalıdır. Alan olarak tuttuğunuz bir 'şu anki kullanıcı' bilgisi, farklı iş parçacıklarında birbirinin verisini ezer; bu çok sık görülen ve zor yakalanan bir hatadır.
İstek ya da oturum başına yeni örnek gerekiyorsa web kapsamları vardır; prototip kapsamı ise her istenişte yeni örnek üretir. Bunları kullanırken tekil bir bean'in kısa ömürlü bir bean'i doğrudan tutamayacağını bilin: ara vekil nesne ya da sağlayıcı kullanılmalıdır.
Yaşam döngüsünde bean oluşturulur, bağımlılıkları verilir, başlatma metodu çalışır; kapanışta yok etme metodu çağrılır. Başlatma için @PostConstruct, kapanışta kaynak bırakmak için @PreDestroy kullanılır. Uzun süren işlemi başlatma metoduna koymayın, uygulama açılışını geciktirir.
Döngüsel bağımlılık ve diğer başlangıç hataları
Uygulama açılırken karşılaşılan hatalar çoğunlukla konteynerin bağımlılık grafiğini kuramamasından gelir. A sınıfı B'ye, B sınıfı A'ya ihtiyaç duyarsa döngüsel bağımlılık oluşur ve yapıcı enjeksiyonunda açılış başarısız olur. Bunu set metoduyla gizlemek hatayı örter, tasarımı düzeltmez. Çözüm genellikle ortak sorumluluğu ayrı bir sınıfa çıkarmak ya da bir tarafı olay yayınlamaya çevirmektir.
Hata mesajını okumak çözümün yarısıdır. 'Bean bulunamadı' mesajı genellikle paket dışında kalmış bir sınıf, anotasyonu unutulmuş bir sınıf ya da kapalı bir profil demektir. 'Birden fazla bean' mesajı yukarıdaki seçim problemidir. Hatanın zincirinde kimin kimi istediği satır satır yazılır; zinciri baştan sona okuyun.
Hata ayıklama için, konteyner içinde hangi bean'lerin oluştuğunu görmek faydalıdır: Actuator'ın bean listesi uç noktası bunu gösterir ancak bu uç noktayı yalnızca geliştirme ortamında açın.
Sık yapılan hatalar
- Alan enjeksiyonunu alışkanlık edinmek. Test ve değişmezlik avantajlarını kaybedersiniz.
- Tekil bean'de kullanıcıya özgü değişken tutmak.
- Her sınıfa bean olarak sahip olmak. Basit değer nesneleri ve veri taşıyıcıları konteynere ait değildir; kendiniz oluşturun.
- Konteynerden bean'i elle istemek (uygulama bağlamına doğrudan erişmek). Bu, enjeksiyonu atlatıp bağımlılığı gizler.
- Bileşen taramasını gereğinden geniş yapmak. Açılış yavaşlar, istenmeyen bean'ler yüklenir.
- Yapılandırma sınıflarında uzun iş mantığı yazmak. Yapılandırma yalnızca bağlama işini yapmalıdır.
- Birden çok gerçekleme varken seçimi rastlantıya bırakmak. Açıkça @Primary ya da @Qualifier kullanın.
Bu beceriyi Mindset programlarında nerede çalışırsınız?
Java Spring Boot ile Backend programı, Spring Core: Dependency Injection ve Bean Yönetimi modülüyle bu konuyu doğrudan ele alıyor; ardından Spring Web MVC ve REST API ile Spring Data JPA modülleri bu temelin üzerine kuruluyor. Program, Java bilen ama Spring'in nasıl çalıştığını sistematik öğrenmek isteyen geliştiriciler için uygun görünüyor.
Kurumsal bir geliştirme ekibi için bu konu kod incelemesinin ortak dilini kurar: enjeksiyon biçimi, kapsam kuralları ve bean seçimi için ekip standardı yazılı hale gelince, yeni katılanlar proje yapısına hızla uyum sağlar.
İlgili Mindset programı
Java Spring Boot ile Backend
Sık sorulan sorular
@Autowired kullanmak yanlış mı?
Yanlış değil ama alan üzerinde kullanımı önerilmez. Yapıcı metot enjeksiyonu alanların değişmez olmasını sağlar ve konteyner olmadan test yazmayı kolaylaştırır. Tek yapıcısı olan bir sınıfta anotasyonu yazmaya da gerek yoktur; Spring yapıcıyı kendiliğinden kullanır.
@Component ile @Bean farkı nedir?
@Component sınıfın üzerine yazılır ve tarama ile bulunur. @Bean ise yapılandırma sınıfındaki bir metodun döndürdüğü nesneyi bean yapar; sınıfın kaynak koduna dokunamadığınızda ya da nesne oluşumunu elle kontrol etmek istediğinizde kullanılır.
Bean'ler neden varsayılan olarak tekildir?
Çoğu servis durumsuzdur, yani çağıran kişiye özgü veri tutmaz. Böyle nesneler için tek örnek bellek açısından verimlidir ve paylaşılabilir. Durum tutan nesneler için istek ya da prototip kapsamı gibi seçenekler vardır.
Bean oluşturulurken çalışan kodu nereye yazmalıyım?
Basit başlangıç işleri için @PostConstruct kullanılır; bağımlılıklar verildikten sonra çalışır. Ancak ağır veri yüklemesi ya da dış çağrı yapmak açılışı geciktirir. Bu tür işleri uygulama hazır olduktan sonra çalışan bir olay dinleyicisine taşıyın.
İlgili rehberler
Kategori: Yazılım Geliştirme