Spring AOP
AOP’nin temel odağı, birbirleriyle keşisen davranışların ayrılması üzerinedir.
OOP’nin yerini almak için değil, onu desteklemek, tamamlamak için geliştirilmiştir.
OOP’deki SOLID’in temel prensiblerinden biri olan Single Responsibility
her sınıfın sadece kendi sorumluluğunu yerini getirmesini söyler.
AOP’de bu prensibi destekleyecek nitelikte bir çalışmadır.
Projemizin modüllerini incelediğimizde, her modülün business logicleri hariç ortak yapılan işlemler vardır. Mesela; loglama, transaction, cacheleme, exception handling vb.
İşte bu verdiğim örnekler kesişen davranışlarımız(yani Cross-Cutting-Concerns) oluyor.
Bu kesişen davranışlar projemizin neredeyse farklı farklı tüm modüllerinde bulunur. Üstelik bu concern’ler her katmanda da bulunabilir.
(Service, Business, Data, Presentation Layer vb.)
AOP ise birbirleriyle kesişen bu davranışların ayrılması gerektiğini söylemektedir.
AOP’nin odak noktası Seperation Of Concerns’dir. Aynı amacı OOP’de Abstraction ile hedeflese de bir yere kadar yapabilmekteyiz.
Kodun business logic’i hariç bulunan concern’leri ayırarak kodun okunulabilirliğini, tekrar kullanılabilirliğini artırıp ve developer’ın tamamen main logic’e yoğunlaşmasını hedeflemektedir.
OOP’de Abstraction yani soyutlamayı kullanarak sınıfların sıkı sıkıya bağlı
olmamasını sağlayabiliriz. Ancak az önce belirtmiş olduğum(loglama, transaction, cacheleme, exception handling) gibi kesişen davranışlarımıza tam bir çözüm bulamamaktadır.
Burada AOP devreye girerek tamamen independent hale gelmeyi sağlıyor.
AOP ile kesişen davranışlarımız kendi içerisinde bağımsız olarak geliştirilebilir ve tekrar tekrar kullanılabilir.
Bir projedeki herhangi bir class’ı düşünelim. Bu class’ın saveUser methodu adından da anlaşılacağı gibi user’i save etmek için yapılmış olsun.
Ancak bu methodun kendi ana logicini uygulamak harici farklı işlemlerde yapmasını engelleyemeyiz. Hem kendi logic’ini yapar.
Hemde loglama, exception handling vb. pek çok işlemi yapar ve kendi logici
hariç işleri de üstlenmiş olur. Birazdan göstereceğimiz örnekler ile AOP
bu işler konusunda yardımcı olarak o methodun üzerinden bazı görevleri alacaktır.
Örnek üzerinde inceleyelim :
Yukarıdaki gibi bir kod bloğunu AOP ile aşağıdaki şekle çevirebiliriz.
AOP sonrası kod ne kadar sade ve anlaşılır bir hale geldi. AOP olmadan kod ship’i save etmek harici yetki kontrolü, loglama, transaction, cacheleme gibi bir sürü operation’ı yönetiyor. Ama AOP sayesinde tamamen kendi business logic’ine yoğunlaşabiliyor.
Şimdi keşisen davranışları nasıl ayıracağız o konuya geldik.
Interceptor’ler yardımıyla araya girebiliriz.
Interceptorlar, bir metodun çağrılma öncesi ve sonrasında işlemler yapabilmemizi sağlayan yapılardır.
Ayrıca kendi specific aspect’lerimizi yazmak istesek bir AspectInterface’i yaparız ve onun içerisinde onAfter ve onBefore methodları tanımlarız.
Daha sonra Cacheleme, Loglama sınıfları yazarak bu AspectInterface’ini
implemente eder. İstediğimiz methodu çağırmadan önce ve sonra çalışacak olan onAfter, onBefore methodlarında ne yapmak istiyorsak onu yazarız. Örneğin ; ana methodumuz çalışmadan önce CacheAspect sınıfımız araya girecek. İlk önce onBefore methodunu sonra ana methodu sonrada onAfter methodunu çağırarak istediğimiz işlemi yapabilmiş olacağız.
Şimdiye kadar method çağrılmadan önce ve sonra yapmak istediklerimizi
yazdık peki araya nasıl giricez?
Araya girmemizi sağlayacak olan Proxy sınıfını yazacağız.
Bu tasarladığımız Proxy sınıfı gerçek sınıfımızı temsil edecek ve ilgili
method call edilmeden önce veya sonrasında istediğimiz işlemleri yapıyor olacak. Bu proxy sınıfı gerçek sınıfımız ile aynı methodlara sahip olacak.
Diyelim ki A sınıfımız var. Bu A sınıfımızın çeşitli methodları var ve bu methodlar içerisinde logicler var.
Ayrıca bu A sınıfımızın AInterface’i bulunmakta.
İşte bu proxy sınıfımız bu AInterface’ini implemente eder. Böylece onun ile birebir aynı methodlara sahip olur.
Sonra bu A sınıfının methodu call edildiğinde araya girer. Ve önce
transaction, loglama,cacheleme gibi işlemleri yapar.
Daha sonra gerçek sınıfın methodunu çağırır. O gerçek sınıfın methodu içerisindeki logic’ler tamamlanır. Gerçek sınıfın methodunun işlemleri de tamamlandıktan sonra bu proxy sınıf method sonrası transaction, loglama,cacheleme vb. işlemleri de yaparak işini bitirir.
Yani örnek bir kod parçası :
Son olarak Spring AOP arka planda neler yapar onu anlatalım.
Biz @Transactional, @Async gibi annotation koyduğumuz methodların
sınıflarının Proxy sınıflarını yaratır. Bu proxy sınıf sayesinde sınıfımızın methodu call edildiğinde araya girebilir. Bu proxy sınıf araya reflection sayesinde girer.
Burada transactional veya async sağlayabilmek için ilgili transactional veya async before aspectini çağırır daha sonra ana sınıfın methodunu call eder. O da işlemini bitirdikten sonra transactional veya async after aspectini çağırarak işlemi bitirir.
Spring anlattığım bu akış sayesinde annotationlarla transactional ve async
desteği verebilir bize.
Bakın yanlış anlaşılma olmasın. Normal transactional ve async’i kendiniz
yöneterek transactional ve async yapabilirsiniz.(Yani transaction.begin() , transaction.commit(), CompletableFuture kullanarak yapabilirsiniz.)
Ancak annotation’larla yapmak isterseniz Spring’in yaptığı işlemler
yukarıda anlattığım gibidir.
Bu akıştan dolayı eğer bir methodu bean üzerinden çağırmazsak, o annotationlu transactional veya async çalışamaz. Çünkü proxy
sınıf devreye(araya) giremez. O şekilde tasarlanmamıştır.
Yani A class’i varsa bu A class’inin X methodu içerisinde Y(@async) methodunu çağırırsa method async olarak çalışmaz. Bunu yapabilmek için
yeni bir B bean’i yaratıp, Y methodunu bean üzerinden çağırarak çalışmasını sağlamalısınız.
Söylediğim gibi Proxy modunda , yalnızca proxy üzerinden gelen external method call’lar intercept edilir. Bu self invocation yaptığımızda , method @Transactional olarak işaretlense bile transacitonal olarak çalışmayacağı anlamına gelir. Eğer illaki aynı bean içerisinden o bean içerisinde bulunan transactional methodu çağırıp bunun çalışmasını istiyorsak transaction mode’unu değiştirmek gerekir.
Son olarak bir örnek daha vererek bitirelim.
Son olarak AspectJ dediğimizde karşımıza çıkan 3 kavramı inceleyip bitirelim.
Concern
Join Point
Pointcut
Aspect
Bir aspect, birden fazla sınıfı kesen bir concern’in(loglama,transaction,vb.) modulerize edilmesidir. Yani advices’ı tutan sınıftır.
Join point
Uygulamamızdaki herhangi bir methoddur. Yani AOP’den bağımsız olan methodlarımızdır.
Örnek olarak public String getName() methodudur
Advice
Join point çağrılmadan önce veya sonra çalıştırılan kodtur.
Before Advice, After Returning Advice, After Advice, Around Advice vb.
Örnek : getNameAdvice() methodudur
Pointcut
Hangi advice kodunun çalıştırılması gerektiğini belirleyen expressiondır.
Örnek : execution(public String getName())
AOP Weaver Nedir?
Aslında interception’ın nasıl çalışacağıdır. Yani bir methodumuzla ilgili olan aspectin araya nasıl gireceğinin yöntemidir.