Gün 1 / Spring Core / Modül 03 — Spring AOP
Ana Portal
Modül 03 Gün 1 Spring Core ~85 dk

Spring AOP

Bu modülde Spring uygulamalarında tekrar eden ortak davranışları business kodun içinden nasıl ayırdığımızı göreceğiz. Logging, audit, transaction, security ve performans ölçümü gibi konuların neden ayrı bir mekanizma istediğini basitten başlayarak anlatacağız; sonra MiniBank üzerinde audit logging davranışını birlikte ekleyeceğiz.

Anlatım
55 dk
MiniBank Case
20 dk
Kapanış
5 dk
Checkpoint
lab-03

AOP’yi “magic” değil, proxy üzerinden çalışan kontrollü bir mekanizma olarak okuyacaksın.

1

Ortak davranışları ayırma

Logging, audit, transaction ve security gibi kodların neden business metodların içine dağılmaması gerektiğini anlayacaksın.

2

Proxy mental modeli

Spring AOP’nin çağrıyı gerçek bean’e göndermeden önce bir proxy üzerinden yakaladığını göreceksin.

3

MiniBank audit altyapısı

MiniBank operasyonlarına @Auditable annotation’ı ile merkezi audit logging davranışı ekleyeceksin.

Kavramları önce basit karşılıklarıyla oturtalım.

AOP konusu ilk bakışta soyut gelebilir. Bu yüzden önce kavramları günlük yazılım geliştirme diliyle netleştirelim; sonra Spring’in içeride nasıl çalıştırdığına geçelim.

AOP

Aspect Oriented Programming, ortak davranışları business koddan ayırma yaklaşımıdır. Spring’de özellikle proxy mekanizmasıyla karşımıza çıkar.

Cross-cutting concern

Birden fazla katmanda tekrar eden ortak ihtiyaçtır. Logging, audit, security ve transaction bunun en yaygın örnekleridir.

Aspect

Ortak davranışı taşıyan sınıftır. Örneğin AuditAspect, audit davranışının merkezi olduğu yerdir.

Advice

Aspect’in çalıştırdığı aksiyondur. Methoddan önce, sonra veya methodun etrafında çalışabilir.

Pointcut

Advice’ın nerede çalışacağını belirleyen ifadedir. “Şu paketteki şu methodlar çalışınca araya gir” demenin yoludur.

Join Point

Aspect’in araya girebileceği noktadır. Spring AOP’de pratikte en önemli join point method çalıştırmadır.

Proxy

Gerçek bean’in önünde duran temsilci objedir. Çağrıyı yakalar, ek davranışı çalıştırır, sonra gerçek methoda gider.

Target Object

Gerçek business logic’in bulunduğu asıl bean’dir. Proxy, sonunda çağrıyı bu objeye iletir.

Self-invocation

Bir bean’in kendi içindeki başka methodu this.method() gibi çağırmasıdır. Bu çağrı proxy’den geçmediği için AOP davranışı beklenmeyebilir.

Basitten başlayıp proxy mantığına kadar gidelim.

3.1 AOP nedir?

AOP, business kodun içine tekrar tekrar yazmak istemediğimiz ortak davranışları ayrı bir yerde toplama yaklaşımıdır.

Bir bankacılık uygulaması düşünelim. Para transferi yapılırken log atmak isteriz. Aynı işlemde audit kaydı tutmak isteriz. Belki yetki kontrolü yapmak isteriz. Belki methodun ne kadar sürdüğünü ölçmek isteriz. Bu ihtiyaçlar sadece transfer metoduna özel değildir; hesap açma, müşteri güncelleme, işlem geçmişi görüntüleme gibi birçok yerde tekrar eder.

Eğer bu ortak işleri her methodun içine tek tek yazarsak business kodun amacı bulanıklaşır. transfer() metodu sadece para transferi yapıyor gibi görünmez; log, audit, güvenlik, performans ölçümü, hata formatlama gibi yan işler de aynı methodun içine doluşur. AOP burada devreye girer ve şunu söyler: “Bu ortak davranışı ayrı bir yerde tanımlayalım, hangi methodlarda çalışacağını da kural olarak belirtelim.”

Basit mental model

AOP, business methodun etrafına görünmez ama kontrollü bir katman koymak gibidir. Method çağrılır; önce ortak davranış çalışır, sonra gerçek business logic çalışır, en sonda gerekiyorsa tekrar ortak davranış çalışır.

01
Caller
Controller veya başka service methodu çağırır.
02
Proxy
Çağrıyı önce Spring proxy objesi yakalar.
03
Advice
Logging, audit veya transaction gibi ek davranış çalışır.
04
Target
Gerçek business method çalıştırılır.
05
Return
Sonuç caller’a döner, gerekirse çıkış logu yazılır.

3.2 Aspect, Advice, Pointcut ve Join Point kavramları

AOP öğrenirken en çok kafa karıştıran şey kavramların isimleri olabilir. Aslında hepsi aynı cümleyi farklı parçalara böler: “Şu methodlar çalışırken, şu ortak davranışı çalıştır.”

Aspect = davranışın evi

Audit loglama davranışını AuditAspect içinde toplarız. Böylece audit ile ilgili kod AccountService, CustomerService veya TransferService içine dağılmaz.

Advice = yapılacak iş

Methoddan önce log yaz, methoddan sonra süreyi ölç, hata olursa audit kaydı oluştur gibi aksiyonlardır.

Pointcut = nerede çalışacak?

@Auditable annotation’ı olan methodlarda çalış veya belirli paketteki service methodlarında çalış gibi kuraldır.

Join Point = yakalanan nokta

Spring AOP için en pratik join point method execution’dır. Yani method çağrısı sırasında araya gireriz.

AuditAspect.java — kavramların kodda karşılığı
@Aspect
@Component
public class AuditAspect {

    @Around("@annotation(auditable)") // Pointcut: @Auditable olan methodlar
    public Object audit(ProceedingJoinPoint joinPoint,
                        Auditable auditable) throws Throwable {
        // Advice: methodun etrafında çalışacak davranış
        long start = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed(); // Join point: gerçek method çalışır
            long duration = System.currentTimeMillis() - start;
            // Audit log burada oluşturulur
            return result;
        } catch (Throwable ex) {
            // Hata audit'i burada oluşturulur
            throw ex;
        }
    }
}

3.3 Spring AOP proxy mantığı

Spring AOP’yi anlamanın en kritik noktası şudur: Spring çoğu durumda gerçek bean’i doğrudan çağırmaz; onun önüne bir proxy koyar.

Proxy’yi bir kapı görevlisi gibi düşünebiliriz. Dışarıdan gelen çağrı önce proxy’ye gelir. Proxy “bu method için aspect çalışmalı mı?” diye bakar. Evetse önce advice’ı çalıştırır, sonra gerçek bean’e geçer. İş bitince dönüşte de tekrar araya girebilir.

Bu mekanizma sayesinde AccountOperationService içinde audit kodu yazmadan, o method çağrıldığında audit log oluşturabiliriz. Business kod sade kalır; audit davranışı merkezi bir aspect içinde yönetilir.

AOP olmadan

Her methodun içine manuel log/audit kodu yazılır. Zamanla business logic ile teknik işler karışır.

AOP ile

Business method sadece işini yapar. Audit/logging davranışı aspect içinde merkezi olarak çalışır.

JDK Dynamic Proxy vs CGLIB

Spring AOP proxy üretirken genellikle iki yol kullanır. Eğer bean bir interface üzerinden proxylenecekse JDK Dynamic Proxy kullanılabilir. Interface yoksa veya class tabanlı proxy gerekiyorsa CGLIB devreye girer. Eğitim sırasında bu ayrımı ezberletmekten çok şunu oturtacağız: hangi yöntem kullanılırsa kullanılsın, çağrının proxy üzerinden geçmesi gerekir.

JDK Dynamic Proxy

Interface tabanlı proxy üretir. Çağrı interface üzerinden temsil edilir.

CGLIB

Class tabanlı proxy üretir. Spring Boot uygulamalarında class proxy davranışıyla sık karşılaşılır.

Senior dikkat

Proxy mantığını bilmeden AOP kullanmak kolaydır; ama bir gün aspect’in neden çalışmadığını anlamak zorlaşır. “Çağrı proxy’den geçti mi?” sorusu AOP debug ederken sorulacak ilk sorudur.

3.4 Advice türleri: @Before, @After, @Around

Advice, aspect’in method çağrısı sırasında ne yapacağını belirler. Her advice tipi farklı bir zamanlama için uygundur. Eğitimde özellikle @Around üzerinde duracağız, çünkü hem methoddan önce hem sonra çalışabilir, süre ölçebilir, hata yakalayabilir ve sonucu kontrol edebilir.

@Before

Method çalışmadan önce devreye girer. Basit giriş logu veya yetki ön kontrolü gibi işler için uygundur.

@After / @AfterReturning

Method tamamlandıktan sonra çalışır. Sonuç başarılıysa ayrı, her durumda ayrı davranış yazılabilir.

@Around

Methodun etrafını sarar. Süre ölçümü, audit, hata loglama ve sonucu dönmeden önce işleme için en esnek yapıdır.

@Around — en esnek advice tipi
@Around("@annotation(auditable)")
public Object measureAndAudit(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    try {
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        log.info("operation succeeded method={} duration={}ms",
                joinPoint.getSignature().getName(), duration);
        return result;
    } catch (Throwable ex) {
        log.warn("operation failed method={} error={}",
                joinPoint.getSignature().getName(), ex.getMessage());
        throw ex;
    }
}

3.5 @Transactional aslında AOP ile neden ilişkilidir?

@Transactional çoğu geliştirici için sadece annotation gibi görünür; ama arkasında proxy üzerinden çalışan bir davranış vardır.

Bir service methoduna @Transactional koyduğumuzda Spring o method çağrısını yakalayabilir. Methoda girmeden önce transaction başlatır, method başarılı biterse commit eder, exception fırlarsa rollback kararı verir. Bu davranışın business methodun içine yazılmaması büyük avantajdır.

Buradaki kritik nokta yine proxy’dir. Transaction davranışı methodun içine compile-time’da yapıştırılmaz. Runtime’da Spring proxy çağrıyı yakalar ve transaction yönetimini methodun etrafına sarar. Bu yüzden @Transactional çalışmadığında ilk kontrol edilecek konulardan biri method çağrısının proxy üzerinden geçip geçmediğidir.

01
Proxy çağrıyı alır
Transactional method dışarıdan çağrılır.
02
Transaction başlar
Spring transaction boundary açar.
03
Business method
Gerçek method çalışır.
04
Commit / rollback
Sonuca göre transaction tamamlanır.

3.6 Self-invocation problemi

Self-invocation, AOP’nin en önemli senior tuzaklarından biridir: aynı class içinden yapılan method çağrısı çoğu durumda proxy’ye uğramaz.

Diyelim ki TransferService içinde public bir method başka bir @Transactional methodu aynı class içinden çağırıyor. Kod okunurken “annotation var, transaction çalışır” diye düşünebiliriz. Ama çağrı this.innerMethod() gibi aynı obje üzerinde yapılıyorsa Spring proxy araya giremez. Çünkü dışarıdan proxy’ye gelen bir çağrı yoktur; obje kendi iç methodunu doğrudan çağırmıştır.

Riskli yaklaşım
@Service
public class TransferService {

    public void transfer() {
        validate();
        saveAuditInNewTransaction(); // aynı class içinden çağrı
    }

    @Transactional
    public void saveAuditInNewTransaction() {
        // Transaction beklenir ama çağrı proxy'den geçmezse çalışmayabilir.
    }
}
Daha temiz yaklaşım
@Service
@RequiredArgsConstructor
public class TransferService {
    private final AuditService auditService;

    public void transfer() {
        validate();
        auditService.saveAudit(); // başka bean üzerinden proxy'ye gider
    }
}

@Service
public class AuditService {
    @Transactional
    public void saveAudit() {
        // Transaction proxy üzerinden uygulanır.
    }
}
Bu modülün en kritik cümlesi

AOP davranışı beklediğin yerde çalışmıyorsa sadece annotation’a bakma; çağrının proxy üzerinden geçip geçmediğine bak.

MiniBank Case 03 — Audit Logging with AOP

Bu case’de MiniBank operasyonlarına merkezi audit logging davranışı ekleyeceğiz. Case bir hata avı değildir; eğitmen doğru yapıyı kod üzerinde gösterecek, sonra katılımcılar aynı pattern’i customer operasyonları için uygulayacak. Görev listesi, doğrulama komutları, kod adımları ve kabul kriterleri Lab Dashboard'da.

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

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

Bu modülden akılda kalması gerekenler

1
AOP ortak davranışları merkezileştirir.

Logging, audit, transaction ve security gibi davranışlar business methodların içine dağılmadan yönetilebilir.

2
Spring AOP proxy tabanlı düşünülmelidir.

Çağrı proxy’den geçerse advice çalışır; proxy bypass edilirse beklenen AOP davranışı çalışmayabilir.

3
@Around en esnek advice tipidir.

Methoddan önce ve sonra çalışabilir, süre ölçebilir, sonucu yakalayabilir ve hata durumunu loglayabilir.

4
@Transactional da proxy davranışıyla ilişkilidir.

Transaction boundary çoğu senaryoda method çağrısının proxy üzerinden yakalanmasıyla uygulanır.

5
Self-invocation senior seviye bir tuzaktır.

Aynı class içinden yapılan çağrı proxy’ye uğramayabilir. Bu yüzden servis sınırları doğru tasarlanmalıdır.

5 kısa kontrol sorusu

1. AOP hangi problemi çözmek için kullanılır?

AOP, birçok method veya katmanda tekrar eden logging, audit, transaction, security gibi ortak davranışları business koddan ayırmak için kullanılır.

2. Aspect, Advice ve Pointcut arasındaki fark nedir?

Aspect ortak davranışın bulunduğu sınıftır. Advice çalışacak aksiyondur. Pointcut ise advice’ın hangi methodlarda çalışacağını belirleyen kuraldır.

3. Spring AOP’de proxy neden önemlidir?

Çünkü Spring AOP çoğunlukla method çağrısını proxy üzerinden yakalar. Çağrı proxy’den geçmezse advice çalışmayabilir.

4. @Around advice ne avantaj sağlar?

Methoddan önce ve sonra çalışabildiği için süre ölçümü, başarılı/başarısız audit, hata yakalama ve sonuç loglama gibi ihtiyaçları tek yerde karşılayabilir.

5. Self-invocation neden risklidir?

Aynı bean içinde bir methodun başka bir methodu doğrudan çağırması proxy’yi bypass edebilir. Bu durumda AOP, @Transactional veya @Async gibi davranışlar beklenen şekilde çalışmayabilir.