Annotation ve Reflection — Spring'in Sihri Nasıl Çalışır?
Önce şunu oku: Interface ve Abstract Class — Rol mü, İskelet mi?
30 saniyede özet
Annotation sınıfa yapıştırılmış bir nottur ve kendi başına hiçbir şey yapmaz. Reflection, programın çalışırken kendi sınıflarına bakıp bu notları okumasıdır; Spring'in bütün 'sihri' bu ikisinin birleşimidir.
Bir depoda kutuların üstüne yapışkan notlar yapıştırıyorsun: “kırılır”, “önce bunu gönder”. Notlar kutuyu değiştirmez; onları okuyan depo görevlisi davranışını değiştirir.
Spring’in “sihir” gibi görünen her yeri, tam olarak bu iki şeyin birleşimidir: bir not ve o notu okuyan biri.
Adım adım oku
- Rafta sıradan üç sınıf duruyor.
- İkisinin üstüne not yapıştırılıyor: @Service ve @Repository. Üçüncüsünde not yok.
- Notlar kendi başına hiçbir şey yapmaz.
- Spring rafı tarar, notlu sınıfların nesnelerini kurar; notsuz sınıfa dokunmaz.
-
Bayt:
@Serviceyazdım, hiçbir yerdenewdemedim, ama nesnem hazır! Bu sihir mi? -
Sen: Bir de kendi notumu yazdım, ama hiçbir şey olmadı.
-
Bayt: Çünkü notu okuyan kimse yok. Spring @Service'i okuyor, senin notunu okuyan henüz yok.
-
Bayt: O zaman notu okuyan kısmı kendimiz yazalım. Sihrin nasıl yapıldığını göreceksin.
Annotation bir not, bir komut değil
@Override, @Service, @Transactional gibi @ ile başlayan her şey bir annotationSınıfa, metoda ya da alana @ ile eklenen bir not. Tek başına kod çalıştırmaz; derleyici ya da Spring gibi bir çerçeve onu okuyup bir şey yapar.@Retention notun ne zamana kadar yaşadığını (SOURCE, CLASS, RUNTIME), @Target nerelere yapıştırılabileceğini belirler.Sözlükte gör →’dır. Sınıfa, metoda ya da alana eklenen bir bilgidir; tek başına hiçbir kod çalıştırmaz.
Onu kimin okuduğu, ne zaman işe yaradığını belirler:
- Derleyici okur:
@Overrideyanlış yazılmış bir metodu derlerken yakalar. - Bir çerçeve okur: Spring, program açılırken
@Serviceve@Transactionalnotlarını okur ve buna göre davranır.
Kafam karıştı, daha basit anlat
Annotation bir etikettir. Etiketi yapıştırmak bir şey yapmaz; etiketi okuyup ona göre davranan biri gerekir. Java’da bu okuyan kişi derleyici ya da Spring gibi bir çerçevedir.
Bir metodun üstüne kendi yazdığın `@Log` annotation'ını ekledin ama hiçbir şey olmadı. En olası sebep nedir?
Reflection: kodun kendine bakması
reflectionProgramın çalışırken kendi sınıflarını, metotlarını, alanlarını ve notlarını incelemesi; nesne kurup metot çağırabilmesi.Spring, JPA ve JUnit gibi çerçeveler senin sınıflarını önceden bilmeden bu yolla bulur ve çalıştırır. Derleyici kontrollerini atladığı için iş kodunda seyrek kullanılır.Sözlükte gör →, programın çalışırken kendi sınıflarına bakabilmesidir: hangi metotları var, hangi notlar yapıştırılmış, kurucusu ne istiyor? Hatta bir nesne kurup bir metodu adıyla çağırabilir.
Class<?> tip = OrderService.class;
for (Method metot : tip.getDeclaredMethods()) { boolean islemli = metot.isAnnotationPresent(Transactional.class); System.out.println(metot.getName() + " → işlemli mi: " + islemli);}
Object nesne = tip.getDeclaredConstructor().newInstance(); // new yazmadan nesneSpring de tam olarak bunu yapar: sınıfları bulur, notlarına bakar, nesnelerini kurar ve ihtiyaç duydukları parçaları verir.
Kafam karıştı, daha basit anlat
Reflection, bir sınıfa neleri olduğunu sorabilmektir. Sınıf da metotlarını ve üstündeki notları söyler. Spring bu cevaplara bakıp ne yapacağına karar verir.
Reflection nedir?
Spring, üzerinde `@Service` olan bir sınıfın nesnesini nasıl oluşturur?
Satır satır: kendi küçük Spring’imiz
Kendi notumuzu yazalım ve onu okuyan küçük bir okuyucu kuralım. Önce bir tahmin:
Bir `@Denetle` annotation'ı yazdın ama başına `@Retention` eklemedin. Program çalışırken `isAnnotationPresent(Denetle.class)` ne döner? Cevabı göster
false. Varsayılan saklama süresi CLASS’tır: not derlenmiş dosyaya yazılır ama çalışma anında reflection’a gösterilmez. Çalışırken okumak için @Retention(RetentionPolicy.RUNTIME) gerekir.
Notu okuyan, çağrıyı yapan
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface Denetle {} for (Method m : servis.getClass().getDeclaredMethods()) { if (m.isAnnotationPresent(Denetle.class)) { System.out.println("denetim: " + m.getName()); m.invoke(servis); }}Debug
not RUNTIME: not çalışma anında da okunabilsin. Bu satır olmasa aşağıdaki kontrol hep false olurdu.
Sol/sağ ok tuşlarıyla da gezebilirsin.
Notu kim, ne zaman görür?
Tohum 170566Oynat ya da adımla: her adımda bir metot incelenir.
Şu an ne oldu?
Retention yok (varsayılan: CLASS) · getDeclaredMethods()
Okuyucu @Denetle notlu metotları bulup çağıracak. İki metotta not var.
Görevler0/3
Not var ama okuyucu hiçbir şey görmesinaçık
İpucu
Varsayılan ayarlar yeter.
Notlu private metodu hiç listelemeaçık
İpucu
Yalnızca public metotları veren yöntem.
İki notlu metodu da çağıraçık
İpucu
Saklama süresi, arama yöntemi ve erişim birlikte.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat.
@Retentionyok: iki metodun da notu var, ama okuyucu hiçbirini görmedi. - “RUNTIME” seç.
odemeAlçağrıldı; privateiadeYapbulundu ama çağrılamadı. - “getMethods()” seç. private metot hiç listelenmedi; Object’ten gelen metotlar listelendi.
- “getDeclaredMethods()“e dön ve “setAccessible(true)“yu aç. İki notlu metot da çağrıldı.
Program ne yazdırır?
Spring bunu nasıl kullanır?
Spring başlarken belirttiğin paketleri tarar ve @Component, @Service, @Repository notlu sınıfları bulur. Sonra reflection ile nesnelerini kurar ve kurucularının istediği parçaları verir.
@Transactional gibi notlar için bir adım daha atar: nesnenin önüne bir proxySpring'in bean'in yerine koyduğu sarmalayıcı nesne. `@Transactional` gibi anotasyonlar burada çalışır — çağrı proxy'den geçmezse hiçbir şey olmaz.Sözlükte gör → koyar. Her çağrı önce proxy’den geçer; proxy notu hatırlar, işlemi açar, metodu çağırır ve işlemi kapatır.
Kafam karıştı, daha basit anlat
Spring açılırken bütün rafları dolaşır, notlu kutuları bulur ve kurar. Bazı notlar için kutunun önüne bir görevli koyar: çağrı önce ona gelir, o da notun istediğini yapar.
`@Transactional` notu, metodun etrafında işlemi nasıl açıp kapatır?
Bir bankada kendi notunu yazmanın en güzel örneği hassas verileri gizlemektir: T.C. kimlik numarası ve IBAN gibi alanlar işaretlenir, log’a yazılırken otomatik maskelenir.
Derinleş · @Sensitive: log'da hassas alanları maskelemek 4 dosya · ~49 satır · ilk okumada atlayabilirsin
Tuzaklar
@Retention unutmak. Varsayılan CLASS olduğu için notun çalışma anında görünmez; okuyucu hiçbir şey bulamaz ve hata da vermez.
Notun kimin tarafından okunduğunu bilmemek. @Transactional bir proxy üzerinden çalışır; sınıfın kendi içinden yapılan çağrıda ya da private bir metotta etkisi olmaz.
Reflection’ı her yerde kullanmak. Derleyicinin kontrollerini atlar, private alanlara uzanabilir ve normal çağrıdan yavaştır. Çerçeve yazarken işe yarar; günlük iş kodunda genellikle gereksizdir.
Log'da hassas alanları maskeleyen kod hiçbir alanı maskelemiyor; T.C. kimlik numaraları açıkça yazılıyor. Hatanın kaynağı hangi satır?
Kendini sına
Bir annotation tek başına kod çalıştırır.
Her annotation'ı onu kimin okuduğuna göre ayır.
Aklında kalacak üç şey
- 1 Annotation yalnızca bir nottur; onu okuyup bir şey yapan bir derleyici, bir araç ya da Spring gibi bir çerçeve olmadıkça hiçbir etkisi yoktur.
- 2 Reflection, programın çalışırken kendi sınıflarını, metotlarını ve notlarını incelemesi, nesne kurup metot çağırmasıdır.
- 3 Kendi annotation'ını çalışırken okumak istiyorsan @Retention(RetentionPolicy.RUNTIME) yazarsın; yoksa not çalışma anında görünmez.
5 kart sonraki derste seni bekliyor