m i.c o
va
dil
CEPHEYE YÖNELİK PROGRAMLAMA (ASPECT ORIENTED PROGRAMMING)
ww w. ja
Mücahit Kurt Hacettepe Üniversitesi Bilgisayar Mühendisliği Bölümü
m
Giriş
i.c o
Bu belgede giriş düzeyinde cepheye yönelik programlamadan bahsedilecektir. AOP tüm dünyada ve ülkemizde geçmişi bayağı eskiye dayanmasına rağmen henüz çok fazla bilinmeyen bir yöntem ama yazılım sistemlerin karmaşıklığı gerçekleştirimlerini yaptıkları gereksinimlere koşut olarak her geçen gün biraz daha arttığı için AOP’nin çözüm ürettiği alanlar insanların başını daha fazla ağrıtmaya başladı ve insanlar AOP ile daha fazla ilgilenmeye başladı. Bu akım en çok uygulama sunucuları alanında oldu, üreticiler AOP projelerini desteklemeye başladı ve JBoss 4.0 uygulama sunucusunda ağırlıklı olarak AOP kullanıldı.
dil
Belge üç ayrı makaleden oluşuyor; birinci makalede cepheye yönelik programlamanın niçin var olduğu, hangi problemlere nasıl çözümler getirdiğinden bahsedilecektir. Ayrıca cepheye yönelik programlamanın değindiği bu problemlere cevap üretmeye çalışan bazı mevcut yöntemlerde açıklanıp karşılaştırma yapılacaktır. İkinci makale giderek popülerliği artan hafif ağırlıklı sistemlerin AOP çözümleri üzerine olacaktır. Şuan için en gelişmiş AOP gerçekleştirimi olan AspectJ in bazı sorunları üzerinde durulup bunlara yeni nesil AOP gerçekleştirimlerinin nasıl cevap ürettiği açıklanacaktır ve AOP nin dünü bugünü ve geleceği üzerindede açıklamalar olacaktır. En son bölüm Spring J2ee Framework’ünün AOP gerçekleştiriminin kullanılması üzerine olacaktır.
ww w. ja
va
Bu belgede Türkçe terimlerin kullanılmasına dikkat edilmesine rağmen özellikle AOP ile ilgili bir çok terimin Türkçe karşılıkları bulunamadı bunun için bu terimler italik harflerle yazıldı.
m
Cepheye Yönelik Programlama (Aspect Oriented Programming (AOP)) Özet
i.c o
Yazılım sistemlerinin çoğu bir çok modül üzerine dağılmış çeşitli ilgilerden (concerns) oluşur. Bu ilgilerin gerçekleştirimi nesneye yönelik teknikler kullanılarak yapılırsa ortaya gerçekleştirimi ve anlaması zor bir sistem çıkar. Yeni cepheye yönelik programlama (aspectoriented programming (AOP)) yöntemleri bu çeşitli modüller üzerine dağılmış ilgilerin modüle edilmesini kolaylaştırıyor. AOP kullanarak tasarlanması, anlaması ve güncellenmesi kolay gerçekleştirimler ortaya çıkarabiliriz. AOP bize yüksek üretkenliği, iyileştirilmiş kaliteyi ve yeni özelliklerin gerçekleştirimini daha iyi yapabilmeyi sağlıyor.
dil
İlgi özel bir amaç, kavram veya ilgi alanıdır. Teknoloji terimleriyle, tipik bir yazılım sistemi çeşitli çekirdek ve sistem-düzeyi ilgiden oluşur. Örneğin bir kredi kartı işleyici sistemin sistem-düzeyi ilgileri loglamayı ele alma, hareket bütünlüğü, yetki kontrolü, güvenlik, performans vb. olurken çekirdek ilgi ödemeleri işlemedir. Bir çok ilgi (crosscutting concerns olarak bilinenler) çoklu gerçekleştirim modüllerini etkilemek eğilimindedir. Mevcut programlama yöntemlerini kullanarak, bir çok modül üzerine yayılmış enine kesen ilgiler(crosscutting concerns) varsa, gerçekleştirimi yapılan sistem tasarımı, anlaması, gerçekleştirilmesi ve geliştirilmesi zor bir sistem olarak sonuçlanır.
va
Cepheye-yönelik programlama (AOP) enine kesen ilgilerin modüle edilmesini sağlayarak ilgileri önceki yöntemlerden daha iyi ayırır. Bu makalede ilk önce enine kesen ilgilerin herhangi karmaşık bir yazılım sisteminde neden oldukları problemler açıklanacak. Sonra AOP çekirdek kavramları açıklanacak ve AOP nin enine kesen ilgiler ile ilgili problemleri nasıl çözdüğü gösterilecektir.
Yazılım programlama yöntemlerinin evrimi
ww w. ja
Bilgisayar bilimlerinin ilk günlerinde, geliştiriciler direk makine-düzeyi kodlama ile program yazıyorlardı. Maalesef programcılar ellerindeki problemden daha çok özel makine komut kümeleri üzerinde düşünmek için zaman harcıyorlardı. Yavaşça kullandığımız makineden soyutlanmamıza izin veren üst düzey dillere doğru göç ettik. Sonra yapısal diller geldi; artık problemlerimizi işimizi yapabilmek için gerekli olan yordamlar düzeyinde ayrıştırabiliyorduk. Ancak, karmaşıklık büyüdü ve daha iyi tekniklere ihtiyaç duyduk. Nesneye-yönelik programlama (NYP) bir sisteme birlikte çalışan nesneler kümesi olarak bakmamıza izin verdi. Sınıflar gerçekleştirim ayrıntılarını ara yüzler altında saklamamıza izin verdi. Çok şekillilik (polymorphism) ilişkili kavramlar için ortak bir davranış ve ara yüz sağladı ve temel kavramların gerçekleştirimine erişmeye ihtiyaç duymaksızın özel davranışları değiştirebilmek için daha özelleştirilmiş bileşenler oluşturulmasına izin verdi. Programlama yöntemleri ve diller makineler ile iletişim kuracağımız yolu tanımlar. Her yeni yöntem problemlerimizi ayrıştırabileceğimiz yeni yollar gösterir: makine kodları, makinebağımsız kodlar, yordamlar, sınıflar vb. Her yeni yöntem sistem gereksinimlerinden programlama yapılarına daha doğal bir eşleme sağlar. Bu yöntemlerin evrimleşmesi bizim yükselen karmaşıklıklarda sistemler üretmemizi sağladı. Bu gerçeğin tersi de eşit olarak doğru olabilir; bizim karmaşıklığı daha çok sistemler oluşturmamıza izin verildi çünkü bu teknikler bizim bunu yapmamıza, kendileri bu karmaşıkların üstesinden gelerek, izin verdi.
i.c o
m
Günümüzde, NYP bir çok yeni yazılım projesi için seçilen yöntem olarak hizmet vermektedir. NYP ortak davranışları modellemek için geldiğinde gücünü göstermişti. Ancak kısaca göreceğimiz gibi veya sizin zaten önceden tecrübe ettiğiniz gibi, NYP bir çok modül – çoğu zaman bağımsız – üzerine dağılmış davranışları ele almada yetersizdir. Zıt olarak, AOP yöntemleri bu boşluğu doldururlar. AOP ’nin, programlama yöntemlerinin evrimindeki sonraki büyük adımı temsil etmesi mümkündür.
Sisteme bir ilgi kümesi olarak bakmak
ww w. ja
va
dil
Karmaşık bir yazılım sistemine çoklu ilgilerin birleştirilmiş gerçekleştirimi olarak bakabiliriz. Tipik bir sistem çeşitli türlerde ilgilerden oluşabilir, bu ilgiler iş mantığı, performans, veri kalıcılığı, loglama ve böcek ayıklama, yetki kontrolü, güvenlik, çok işletim dizili emniyet, hata kontrolü vb. olabilir. Ayrıca geliştirme süreci ilgilerini de sayabiliriz bunlar arasında kavranabilirlik, güncellenebilirlik, takip edilebilirlik ve kolay evrimleştirebilme olabilir. Şekil 1 bir sistemi çeşitli modüller tarafından gerçekleştirimi yapılan ilgiler kümesi olarak gösteriyor.
Şekil 1. İlgilerin bir kümesi olarak gerçekleştirim modülleri
Şekil 2 gereksinimler kümesini prizma içinden geçen bir ışık demeti olarak gösteriyor. Gereksinimler ışık demetini ilgi-tanımlama prizmasından geçiriyoruz, bu prizma her ilgiyi ayırıyor. Aynı bakış geliştirme süreci ilgileri için de uzatılabilir.
m i.c o
dil
Şekil 2. İlgi ayrıştırma: Prizma benzeşimi
Bir sistemdeki enine kesen ilgiler
va
Bir geliştirici çoklu gereksinimlere cevap olarak bir sistemi oluşturur. Biz bu gereksinimleri geniş olarak çekirdek modül-düzeyi gereksinimler ve sistem-düzeyi gereksinler olarak sınıflandırabiliriz. Sistem-düzeyi gereksinimlerinin bir çoğu birbirlerini ve modül-düzeyi gereksinimleri kesme eğilimindedir. Sistem-düzeyi gereksinimler ayrıca bir çok çekirdek modülü çaprazlama kesme eğilimdedir. Örneğin, tipik bir kurumsal uygulama yetki kontrolü, loglama, kaynak havuzlama, yönetim, performans ve depo yönetimi gibi ilgilerden oluşur. Ve bunların her biri çeşitli alt sistemleri çaprazlama keser. Örneğin bir depo yönetimi ilgisi her durumlu iş nesnesini etkiler. Daha basit fakat daha somut bir örneği düşünmek gerekirse, bir iş mantığını sarmalayan sınıfın iskelet gerçekleştirimini düşünebiliriz:
ww w. ja
public class İşSınıfı extends BaşkaİşSınıfı { // çekirdek veri elemanları // diğer veri elemanları : Log stream, veri-tutarlılık bayrağı
// taban sınıfın yöntemlerini yeniden yaz
public void birİşYap (İşBilgisi bilgi) { // yetkiden emin ol // verinin gereken bilgiyi taşıdığından emin ol
// diğer işletim dizileri eriştiğinde verinin tutarlı // kalmasını sağlamak için nesneyi kitle // ön belleğin açık olduğundan emin ol // işlemin başlangıcını logla // ==== Çekirdek işlemleri yap ====
// nesnedeki kilidi aç } // yukarıdakine benzer diğer işlemler
}
i.c o
public void kayıtEt (KalıcıDepo kd) { }
m
// işlemin bittiğini logla
public void yükle (KalıcıDepo kd) { }
}
Enine kesen ilgiler problemi
dil
Yukarıdaki kodda en az üç mesele üzerinde düşünmeliyiz. Birincisi, diğer veri üyeleri bu sınıfın çekirdek ilgilerine ait değildir. İkincisi, birİşYap() yönteminin gerçekleştirimi sadece çekirdek işlemleri yapmıyor bu yöntemde ayrıca loglama, yetki kontrolü, çoklu işletim dizisi emniyeti, verinin taşıdığı bilgiyi geçerleme ve ön bellek yönetim ilgilerinin gerçekleştirimi yapılıyor. Ek olarak bu ilgilerin çoğunun gerçekleştirimi diğer sınıflar içinde benzer yolla yapılacak. Üçüncüsü, eğer kayıtEt() ve yükle() kalıcılık yönetim işi yapacaksa sınıfın çekirdek bölümünü mü oluşturmalıdır sorunu çok açık değil.
va
Enine kesen ilgiler bir çok modül üzerine dağılmalarına rağmen, mevcut gerçekleştirim teknikleri bu gereksinimleri tek-boyutlu yöntemler, gereksinimlerin gerçekleştirim eşlemelerini tek boyut boyunca zorlama, kullanarak gerçekleştirmek eğilimindedir. Bu tek boyut çekirdek-düzeyi gerçekleştirim olma eğilimindedir. Geri kalan gereksinimler bu baskın boyutta etiketlenirler. Diğer bir deyişle, gereksinim uzayı n-boyutlu bir uzay, oysa gerçekleştirim uzayı tek-boyutludur. Bu, uygunsuz gereksinim-gerçekleştirim eşlemesindeki uyuşmayan sonuçlara benziyor.
ww w. ja
Belirtiler Enine kesen ilgilerin mevcut yöntemler kullanılarak yapılan sorunlu gerçekleştirimini bize bir kaç belirti gösterebilir. Bu belirtileri geniş olarak 2 türde sınıflandırabiliriz: •
•
Kod Karıştırma : Bir yazılım sistemindeki modüller bir çok gereksinimle eş anlı olarak etkileşebilir. Örneğin, geliştiriciler çoğu zaman iş mantığı, performans, eş zamanlama, loglama ve güvenlik hakkında aynı anda düşünürler. Gereksinimlerin kalabalığı her ilginin gerçekleştiriminden bir elemanın varlığının eş anlı bulunması demektir buda kod karıştırma olarak sonuçlanır. Kod dağıtma : Enine kesen ilgiler bir çok modül üzerine dağıldıkları için, ilgili gerçekleştirimleri de bu modüller üzerinde dağılırlar. Örneğin, veritabanı kullanan bir sistemde, performans ilgisi veritabanına erişen bütün modülleri etkileyebilir.
Saklı olan anlamlar Kod karıştırma ve dağıtmanın ikisi birlikte bir yazılım sisteminin tasarımını ve geliştirilmesini bir çok yönden etkiler: •
Zayıf izlenebilirlik: Bir çok ilgiyi eş anlı gerçekleştirmek ilgi ve gerçekleştirimi arasındaki benzerliği kapatır ve bu ikisi arasında eşleme yapmak zorlaşır.
• •
m
•
Daha düşük üretkenlik: Bir çok ilgiyi aynı anda gerçekleştirmek geliştiricinin dikkatini ana ilgiden daha önemsiz olan ilgilere doğru kaydırır ve bu da daha düşük üretkenliğe sebep olur. Daha az yeniden kullanılabilir kod: Bir modül bir çok ilginin gerçekleştirimini yapıyor, benzer işlevselliklere ihtiyaç duyan diğer sistemler modülü kullanamayabilir. Düşük kod kalitesi: Kod karıştırma saklı problemleri olan kodlar üretmeye sebep olur. Ayrıca aynı anda bir çok ilgiyi hedef almak bu ilgilerden birinin yada bir çoğunun gerçekleştirimin yeterince etkin yapılamamasına neden olabilir. Daha zor evrim: Sınırlı bir bakış ve kısıtlanmış kaynaklar çoğu zaman sadece mevcut ilgiye hitap eden tasarımlar üretir. Gelecek gereksinimlere hitap edebilmek gerçekleştirim üzerinde yeniden çalışılmasını gerektirecektir. Ve eğer gerçekleştirim modularize değilse bu bir çok modüle dokunmamızı gerektirecektir. Bir çok alt sistem üzerinde değişiklik yapmak tutarsızlıklara neden olabilir. Ayrıca bu gerçekleştirim değişikliklerinin hataya sebep olup olmadığını anlayabilmek için teste hatırı sayılır bir zaman ayırmamız gerekecektir.
i.c o
•
va
dil
Mevcut cevap Bir çok sistem enine kesen ilgiler içerdiği için gerçekleştirimleri modularize hale getirmek için bazı tekniklerin ortaya çıkması şaşırtıcı değildir. Bu tekniklerden bazıları arasında mix-in sınıflar, tasarım örüntüleri(design patterns) ve alana özel çözümler(domain-specific solutions) vardır. Mix-in sınıflar ile, örneğin, bir ilginin son gerçekleştirimini erteleyebilirsiniz. Birincil sınıf bir mix-in sınıf varlığı içerir ve sistemin diğer parçalarının bu varlığı kurmasını sağlar. Örneğin, bir kredi kartı işleme örneğinde, iş mantığının gerçekleştirimini yapan sınıf bir loglayıcı mix-in oluşturur. Sistemin bir başka parçası uygun loglama türünü alabilmek için bu loglayıcıyı kurabilir. Örneğin bir kütük sistemi veya mesajlaşma orta katmanı kullanılarak loglama yapmak için kurulabilir. Loglamanın tabiatı şimdi ertelenmesine rağmen, birleştirici yinede bütün log noktalarında loglama işlemlerini yaptırmak ve loglama bilgisini kontrol etmek için gerekli olan kodu içerir.
ww w. ja
Visitor ve Template Method gibi davranışsal örüntüler gerçekleştirimi ertelememizi sağlar. Ancak mix-in deki durum gibi işlemin kontrolü – visiting mantığını veya template yöntemlerini uyandırmak – yine ana sınıflarda kalır. Framework ler ve uygulama sunucuları gibi alana özel çözümler geliştiricilerin bazı enine kesen ilgileri module bir yolla ele alabilmelerini sağlar. Örneğin Enterprise JavaBeans(EJB) mimarisi güvenlik, yönetim, performans ve kap (container) tarafından yönetilen kalıcılık gibi bazı enine kesen ilgileri ele alabilir. Deployment geliştiricileri veritabanına bean-veri eşleme gibi deployment a özgü sorunlara odaklanırken bean geliştiricileri iş mantığı üzerinde odaklanırlar. Bean geliştiricileri kalıcılık katmanındaki bir çok parça için depolamaya özgü niteliklerin farkında olarak bir şey yapmaz, bu durumda kalıcılık enine kesen ilgilerinin gerçekleştirimi XML tabanlı tanımlama kütükleri kullanılarak yapılır. Alana özel çözüm özel problemi çözmek için özelleştirilmiş mekanizmalar sunar. Alana özel çözümlerin bir dezavantajı, geliştiriciler her çözüm için yeni teknikler öğrenmek zorundadırlar. Çünkü bu çözümler alana özeldir, enine kesen ilgiler direk olarak ele alınamaz planlanmayan (ad hoc) cevaplar gerekir.
Mimarın İkilemi
İyi sistem mimarları yamalı bir gerçekleştirimden kaçınmak için şimdiki ve gelecekteki gereksinimleri düşünürler. Ancak bu durum bir probleme sebep olur. Geleceği düşünmek zor iş. Eğer gelecek enine kesen ilgileri kaçırırsak yani isabet ettiremezsek sistemin bir çok parçasını değiştirmek yada gerçekleştirimini yeniden yapmak zorunda kalırız. Diğer yandan
m
düşük olasılıklı gereksinimler üzerinde çok fazla odaklanırsak aşırı tasarlanmış karmaşık bir sistem ortaya çıkabilir. Böylece sistem mimarları için ikilem: Ne kadar tasarım aşırı tasarımdır? Az tasarıma mı aşırı tasarıma mı doğru eğilmeliyim?
i.c o
Örneğin, bir mimar başlangıçta ihtiyaç olmayan bir loglama mekanizmasını sisteme dahil etmelimidir. Eğer etmeliyse, loglama noktaları nereler olacaktır ve hangi bilgiler loglanacaktır. Benzer bir ikilem, iyileştirme (optimization)-ilişkili gereksinimler için oluşur – performans ile, biz ilerideki dar boğazları nadiren biliriz. Sistemi inşa etmek için genel yaklaşım; önce sistemin taslağını çıkar ve sonra performansı iyileştirmek için sistem kullanılırken yeni parçalar ekle. Bu yaklaşım potansiyel olarak taslak da ki bir çok sistem parçasını değiştirmeyi gerektirir. Zamanla kullanılan parçaların değiştirilmesine bağlı olarak yeni dar boğazlar ortaya çıkabilir. Yeniden kullanılabilir kütüphane mimarlarının işi daha zor çünkü kütüphane için bütün kullanım senaryolarını hayal etmek daha zor bir iştir.
AOP nin Temelleri
dil
Özet olarak, mimar sistemin ele almaya ihtiyaç duyabileceği her mümkün ilgiyi nadiren bilir. Ele alınmadan önce gereksinimlerin bilinmesine rağmen bir gerçekleştirimi oluşturabilmek için gerekli olan özel ihtiyaçlar tamamen mevcut değildir. Böylece mimarlar az / aşırı tasarım ikilemi ile karşılaşırlar.
va
Buraya kadarki olan tartışmada enine kesen ilgilerin gerçekleştirimlerinin modularize bir şekilde yapılmasının daha uygun olacağını söyledik. Araştırmacılar bu işi “ilgilerin ayrılması” diye daha genel bir konu altında gerçekleştirebilmek için çeşitli çalışmalar yapmışlar. AOP bu işi yapan yöntemlerden bir tanesi. Yukarıda bahsedilen problemlerin üstesinden gelmek için ilgilerin açık bir şekilde ayrılması gerekiyor ve AOP bunun için çabalıyor.
ww w. ja
AOP , çekirdeğinde, ayrık ilgileri bağımlılığın son derece az olduğu bir biçimde gerçekleştirmemizi ve sonuç sistemi oluşturabilmek için bunları birleştirmemizi sağlıyor. Yani AOP enine kesen ilgilerin az bağımlı ve modularize edilmiş gerçekleştirimlerini kullanarak sistemleri oluşturuyor. NYP, zıt olarak, genel ilgilerin (common concerns) az bağımlı, modularize edilmiş gerçekleştirimlerini kullanarak sistemler oluşturuyor. AOP deki module etme birimi aspect olarak adlandırılıyor, bu genel ilgilerin NYP deki gerçekleştirimlerinin sınıf olarak adlandırılması gibidir. AOP de üç ayrık geliştirme adımı vardır:
1. Aspectual ayrıştırma: Gereksinimleri enine kesen ilgiler ve genel ilgiler olarak tanımlayabilmek için ayrıştırma. Modul-düzeyi ilgileri sistem-düzeyi enine kesen ilgilerden ayırırız. Örneğin daha önce bahsedilen kredi kartı modülü örneğinde, üç ilgi tanımlayabiliriz: çekirdek kredi kartı işleme, loglama ve yetki kontrolü. 2. İlgi gerçekleştirimi: Her ilgiyi ayrı olarak gerçekleştir. Kredi kartı işleme örneği için, çekirdek kredi kartı işleme birimi loglama birimi ve yetki kontrolü birimi gerçekleştirimleri yapabiliriz. 3. Aspectual yeniden birleştirme: Bu adımda bir aspect bütünleştiricisi modularizasyon birimlerini – aspect oluşturarak yeniden birleştirme kurallarını belirler. Yeniden birleştirme işlemi, ayrıca örme(weaving) veya bütünleştirme olarak da bilinir, son sistemi birleştirmek için bu yeniden birleştirme kuralları bilgisini kullanır. Kredi kartı işleme örneği için, AOP gerçekleştirimi tarafından sağlanan bir dille, her işlemin başlangıcının ve tamamlanışının loglanmasını belirtebiliriz. Ayrıca her işlemin iş mantığı ile çalışmadan önce yetki kontrolünü temizlemesini belirtebiliriz.
m i.c o
dil
Şekil 3. AOP geliştirme aşamaları
AOP enine kesen ilgileri ele alma yöntemleriyle NYP den bir çok yönden ayrılır. AOP de her ilginin gerçekleştirimi diğer ilgilerin onun üzerinde işlem yaptığının (aspecting) farkında değildir. Örneğin, kredi kartı işlem modülü diğer ilgilerin onun işlemleri üzerinde loglama ve yetki kontrolü yaptığının farkında değildir. Bu NYP den kayan güçlü bir modeli gösterir.
Örme örneği
va
Not: Bir AOP gerçekleştirimi başka bir programlama yöntemini kendi taban yöntemi olarak seçebilir, taban sistemin faydaları da değişmeden kalır. Örneğin bir AOP gerçekleştirimi genel ilgilerin gerçekleştirimi daha iyi yapıldığı için NYP yi taban sistem olarak seçebilir. Bu gerçekleştirimle genel ilgiler her tanımlı ilgi için NYP tekniklerini kullanabilir. Bu yordamsal bir dilin bir çok NYP dili için taban dil olarak kullanılmasına benzer.
ww w. ja
Örücü, işlemci, ayrık ilgileri örme olarak bilinen bir işlemde bir araya toplar. Örücü diğer bir deyişle farklı çalışma-mantığı parçalarını ona sağlanan bazı kriterlere göre bir araya getirir. Kod örmeyi göstermek için kredi kartı işleme sistemi örneğimize bakalım. Kısalık için sadece kredi ve borç işlemlerini düşünelim. Ayrıca uygun bir loglayıcının bulunduğunu varsayalım. Aşağıdaki kredi kartı işleme modülünü düşünelim:
public class KrediKartıİşleyicisi { public void borç(KrediKartı kart, Birim miktar) throws GeçersizKartHatası, YetersizBakiyeHatası, KartZamanAşımınaUğramışHatası { // Borç verme mantığı } public void kredi(KrediKartı kart, Birim miktar) throws GeçersizKartHatası { // Kredi mantığı }
}
public interface Loglayıcı { public void log(String mesaj); }
m
Ayrıca aşağıdaki loglama ara yüzünü düşünelim:
i.c o
İstenilen birleştirme aşağıdaki örme kurallarını gerektiriyor, burada doğal dille açıklanmıştır: 1. Her genel (public) işlemin başlangıcını logla 2. Her genel işlemin tamamlanışını logla 3. Her genel işlem tarafından fırlatılan bütün hataları logla
Örücü sonra bu örme kurallarını ve ilgi gerçekleştirimlerini aşağıdaki birleştirilmiş kodun dengini üretmek için kullanacaktır:
public class LoglamalıKrediKartıİşleyicisi { Loglayıcı _loglayıcı;
va
dil
public void borç(KrediKartı kart, Para miktar) throws GeçersizKartHatası, YetersizBakiyeHatası, KartZamanAşımınaUğramışHatası { _loglayıcı.log("KrediKartıİşleyicisi.borç(KrediKartı, Para)’nın başlangıcı " + "Kart: " + kart + " Miktar: " + miktar); // Borç Mantığı _loglayıcı.log("KrediKartıİşleyicisi.Borç(KrediKartı, Para)’nın tamamlanışı " + "Kart: " + kart + " Miktar: " + miktar); }
ww w. ja
public void Kredi(KrediKartı kart, Para miktar) throws GeçersizKartHatası { System.out.println("Kredi Verme"); _logger.log("KrediKartıİşleyicisi.Kredi(KrediKartı, Para)’nın başlangıcı" + "Kart: " + kart + " Miktar: " + miktar); // Kredi mantığı _logger.log("KrediKartıİşleyicisi.Kredi(KrediKartı, Para)’nın tamamlanışı " + "Kart: " + kart + " Miktar: " + miktar); }
}
AOP dillerinin anatomisi
Bütün diğer programlama yöntemleri gerçekleştirimleri gibi, bir AOP gerçekleştirimi de iki parçadan oluşur: bir dil belirtimi ve bir gerçekleştirim. Dil belirtimi dilin yapısını ve söz dizimini tanımlar. Dil gerçekleştirimi dil belirtimine göre kodun doğruluğuna bakar ve onu
AOP dil belirtimi Daha üst bir düzeyde bir AOP dili iki bileşeni belirler:
•
İlgilerin gerçekleştirimi: Ayrık bir gereksinimi kod içine eşleriz böylece derleyici onu çalıştırılabilir kod şekline çevirebilir. İlgilerin gerçekleştirimleri yordam belirtimleri biçiminde olabilir böylece AOP ile C, C++, Java gibi geleneksel dilleri kullanabiliriz. Kural belirtimlerini örme: Son sistemi oluşturmak için birbirinden bağımsız olarak gerçekleştirimi yapılmış ilgileri nasıl birleştireceğiz. Bu amaç için, bir gerçekleştirim son sistemi farklı gerçekleştirim parçalarını birleştirerek oluşturmak için belirtimi yapılmış kurallara bunun içinde bir dili kullanmaya yada oluşturmaya ihtiyaç duyar.Örme kurallarını belirlemek için gerekli olan dil gerçekleştirim dilinin bir uzantısı veya tamamen farklı bir şey olabilir.
i.c o
•
m
hedef makine’nin çalıştırabileceği biçime çevirir. Bu bölümde bir AOP dilinin bölümleri ve parçaları açıklanacaktır.
dil
AOP dil gerçekleştirimi AOP dili derleyicileri iki mantıksal adımı yerine getirirler: 1. Ayrık ilgileri birleştir 2. Sonuç bilgiyi çalıştırılabilir kod biçimine çevir
ww w. ja
va
Bir AOP gerçekleştirimi örücüyü çeşitli yollarla gerçekleştirebilir, bunlardan birisi kaynakkaynağa çevirmedir. Burada, örülmüş kaynak kodu üretmek için ayrı aspect ler için yazılmış kaynak kodu ön işlemeye alırız. Sonra AOP derleyicisi son çalıştırılabilir kodu üretmek için bu çevrilmiş kodu taban dil derleyicisine yedirir. Örneğin, bu yaklaşımı kullanarak, Java-tabanlı bir AOP gerçekleştirimi ayrı aspect leri önce Java kaynak koduna çevirir, sonra Java derleyicisinin bu kodu bayt kodlarına çevirmesini sağlar. Aynı yaklaşım bayt kod düzeyinde örme de yapabilir; hepsinden sonra, bayt kod hala bir çeşit kaynak koddur. Hatta kullanılan çalıştırma sistemi – bir sanal makine (Virtual Machine) (SM) gerçekleştirimi – aspect lerin farkında olabilir. Java-tabanlı AOP gerçekleştirimi için bu yaklaşımı kullanma, örneğin, SM önce örme kurallarını yükler sonra bu kuralları sonradan yüklenmiş sınıflara uygular. Diğer bir deyişle, SM just-in-time aspect örme yapabilir.
AOP nin faydaları
AOP, kod karıştırmanın ve kod dağıtmanın neden olduğu daha önce bahsedilen problemlerin üstesinden gelmemize yardım eder. AOP nin bize sağladığı bazı özel faydalarda şunlardır: •
•
•
Enine kesen ilgilerin modularize gerçekleştirimi: AOP her ilgiyi minimum bağımlılıkla ayrı olarak ele alır ve bu enine kesen ilgilerin varlığına rağmen modularize olan bir gerçekleştirimle sonuçlanır. Bu gerçekleştirim en az bağımlı kodla bir sistemi üreten bir gerçekleştirimdir. Her ilginin gerçekleştiriminin ayrı olması kod düzensizliğini azaltmaya yardım eder. Ayrıca bir sistemin modularize gerçekleştirilmesi sistemin anlaşılabilirliğini ve güncellenmesini kolaylaştırır. Zamanla geliştirmesi kolay sistemler: Aspected modüller enine kesen ilgilerin farkında olamayabildikleri için yeni aspect ler oluşturarak sisteme daha yeni işlevselliklerin eklenmesi kolaydır. Hatta, sisteme yeni bir modül eklendiği zaman, varolan aspect ler onları enlemesine keser ve bu tutarlı bir evrimin oluşmasına yardım eder. Tasarım kararlarının geç bağlanması: Mimarların az / aşırı tasarım ikilemini hatırlayalım. AOP ile bir mimar gelecek gereksinimler için karar vermeyi erteleyebilir ve bunların gerçekleştirimini ayrı aspect ler olarak yapabilir.
Daha çok yeniden kullanımı: Çünkü AOP her aspect i ayrı bir modül olarak gerçekleştirir, her ayrı modül daha az bağımlıdır. Örneğin, farklı bir gereksinimle ayrı bir loglama aspect inde veritabanı ile modül etkileşimini kullanabiliriz. Genel olarak, az bağımlı gerçekleştirim daha yüksel kod yeniden kullanılabilirliğinin anahtarıdır ve AOP NYP ile yapabildiğimizden daha az bağımlı gerçekleştirimler ortaya çıkarmamızı etkin kılar.
i.c o
m
•
AspectJ: Java için bir AOP gerçekleştirimi
Aspectj, Xerox PARC dan Java için bedava olarak mevcut olan bir AOP gerçekleştirimidir, genel amaçlı bir AO (aspect-oriented) Java uzantısıdır. AspectJ ayrı ilgilerin gerçekleştiriminde dil olarak Java yı kullanır ve örme kuralları için Java ya uzantılar tanımlar. Bu kurallar pointcuts, birleşme noktaları(join points), advice ve aspect isimleri altında belirtilir. Birleşme noktaları bir programın çalışmasındaki özel noktaları tanımlar, pointcut bir dil yapısıdır ve birleşme noktalarını belirtir, advice pointcut larda çalıştırılacak olan aspect gerçekleştirim parçasını tanımlar ve aspect bu primitive leri birleştirir.
dil
Ek olarak, AspectJ ayrıca diğer aspect ler ve sınıfların çeşitli yollarla aspect haline getirilmesine izin verir. Yani, taban sınıfların ve ara yüzlerin gerçekleştirimini yapmak için bir sınıf tanımladığımız gibi yeni veri üyeleri ve yeni yöntemler tanıtabiliriz. AspectJ’in örücüsü – bir aspect derleyicisi – farklı aspect leri birleştirir. AspectJ derleyicisi tarafından oluşturulan son sistem saf Java bayt kodlarıdır ve uyumlu herhangi bir JSM (Java Sanal Makinesi) de çalışabilir. AspectJ’in hata ayıklayıcı (debugger) ve bazı IDE ler ile bütünleşmesini sağlayan araçları da vardır.
va
Aşağıda, yukarıda doğal dille tanımladığımız örücü için loglama aspect nin bir AspectJ gerçekleştirimi var. Burada dikkat edilmesi gereken nokta kredi kartı işlemenin loglama hakkında hiçbir şey bilmediğidir.
public aspect LogKrediKartıİşleyicisiİşlemleri { Loglayıcı loglayıcı = new StdÇıktıLoglayıcı();
ww w. ja
pointcut publicOperation(): execution(public * KrediKartıİşleyicisi.*(..)); pointcut publicOperationCardAmountArgs(KrediKartı kart, Para miktar): publicOperation() && args(kart, miktar);
before(KrediKartı kart, Para miktar): publicOperationCardAmountArgs(kart, miktar) { logOperation("Başlama", thisjoin point.getSignature().toString(), kart, miktar); } after(KrediKartı kart, Para miktar) returning: publicOperationCardAmountArgs(kart, miktar) { logOperation("Tamamlanma", thisjoin point.getSignature().toString(), kart,
m
miktar); }
i.c o
after (KrediKartı kart, Para miktar) throwing (Exception e): publicOperationCardAmountArgs(kart, miktar) { logOperation("Hata " + e, thisjoin point.getSignature().toString(), kart, miktar); }
AOP ye ihtiyacım varmı?
dil
private void logOperation(String durum, String işlem, KrediKartı kart, Para miktar) { loglayıcı.log(durum + " " + işlem + " Kart: " + kart + " Miktar: " + miktar); } }
AOP nizi alın!
va
AOP sadece tasarım noksanlıklarına mı hitap ediyor? AOP de, her ilgi gerçekleştirimi kendisinin başka ilgiler tarafından aspect lendiği gerçeğinin farkında değildir. Bu farkında olmama AOP yi NYP den ayırır. AOP de birleştirmenin akışı enine kesen ilgilerden ana ilgiye doğrudur oysa NYP tekniklerinde akış ters yöndedir. Ancak şu da vardır ki NYP ve AOP çok iyi bir biçimde beraber olabilir. Örneğin bir mix-in sınıfı AOP mizin uygunluk düzeyine bağlı olarak hem AOP hemde NYP ile bir birleşim olarak kullanabiliriz. Her iki durumda da bir enine kesen ilginin gerçekleştirimini yapan mix-in sınıf kendisinin elle birleştirilmiş bir sınıf veya kural tabanlı birleştirme kullanarak oluşturulmuş bir aspect in parçası olduğu bilgisine ihtiyaç duymaz. Örneğin, bir loglama ara yüzünü bazı sınıflar için bir mix-in veya diğerleri için bir loglama aspect i olarak kullanabiliriz. Böylece NYP den AOP evrimsel bir yol olur.
ww w. ja
Bu makalede enine kesen ilgiler ile ilgili problemleri, onları gerçekleştirmek için var olan mevcut yolları ve onların noksanlıklarını ele aldık. Ayrıca AOP nin bu problemlerin üstesinden nasıl geldiğini inceledik. AOP yöntemi enine kesen ilgilerinin gerçekleştirimlerini modularize ediyor ve daha iyi daha hızlı bir yazılım gerçekleştirimi üretiyor. Eğer sizin yazılım sisteminizde bir çok enine kesen ilgiden oluşuyorsa, sizde AOP, onun gerçekleştirimi ve faydaları hakkında daha fazla şey öğrenmeyi düşünmelisiniz. Bu makale www.javaworld.com adresindeki Ramnivas Laddad a ait “I want my AOP!, Part 1 Separate software concerns with aspect-oriented programming” adlı makaleden cevrilmiştir.
m
İkinci-nesil aspect oriented programming
i.c o
AOP frameworklerinin yeni ürünleri ile Advice ları dinamik olarak uygulanması
dil
Özet AOP güçlü bir şekilde modularize edilmiş programlar sunarken ve Java platformu için sağlam, zengin özelliklere sahip gerçekleştirimler AspectJ de mevcutken, AOP henüz ortalama bir Java programcısının araç kutusuna girebilmiş değildir. AOP şimdiye kadar hep konferanslarda ilginç bir merak konusu ve herkesin yapacaklar listesinde hakkında daha fazla şey öğrenilecek bir şey olarak kalmıştır. Sonra hafif ağırlıklı (lightweight), saydam uygulama sunucularına olan ilgi dalgası ile birlikte ortaya, geliştiricilere advice ları öncekinden daha devingen ve esnek bir biçimde uygulama imkanı veren özellikleri ile birlikte yeni AOP gerçekleştirimleri çıktı. Bu yazıda yeni AOP gerçekleştirimleri ile eskileri karşılaştırılıyor, yeni özelliklerin oturduğu yerler açıklanıyor ve Java platformunda AOP nin geleceği inceleniyor.
va
Modülarizasyon programlamayı mümkün kılıyor. Hesaplamanın tarihi boyunca düzenleme araçlarının geçit töreni – üst-düzey dil, yordam, nesne – bizim yükselen bir oranda daha açık ve güçlü kodlar yazmamızı sağlamıştır. Bu aynen bilgisayar donanımında olduğu gibi, yeteneklerimiz iyileştiği zaman çıtayı biraz daha yükseltiyoruz ve şimdi yirmi birinci yüz yıldayız ve hala daha çabuk ve daha ucuza geniş programlar üretmek için çaba sarf ediyoruz. Sonraki adım ne, bizim yeteneklerimizi bir sonraki düzeye alacak programlarımızı yapılandıracak yeni yol?
ww w. ja
AOP bu soruya cevap girişimlerinden bir tanesi. 1990 ların sonlarında Xerox PARC (uğurlu bir soy!) da düşünüldü, AOP sıradan bir program boyunca tekrar eden kod satırlarına karşılık gelen enine kesen ilgileri modularize etmek için tasarlanmıştır. AOP tüm bu enine kesen ilgileri tek bir yer içince topluyor, bir AOP yapısı sınıfa benzer olarak advice olarak biliniyor. AspectJ de ilk olarak Xerox PARC’dan çıktı ve şimdi Eclipse kurumu bünyesinde geliştiriliyor, AspectJ Java için bir AOP gerçekleştirimidir. AspectJ dikkate değer yayımlar boyunca giden ve bazı üçüncü parti araçlar tarafından da desteklenen iyi tasarlanmış sağlam bir framework tür. Son zamanlarda, uygulama sunucusu tasarımcıları şunu anladılar ki – AOP tarafları yıllardır söylüyor – AOP, uygulama sunucularının remoting, kalıcılık ve hareket gibi işlevselliklerini gerçekleştirmek için doğal bir yol olarak görünüyor, eğer gerçekleştirimi eşit olarak devingen ise AOP Java platformunun devingen ortamlarında daha kolay kullanılabiliyor.
AspecJ ile eski AOP
AOP orta katmandaki bir framework te şöyle kullanılabilir: Framework ümüzdeki hizmetlere istemcinin bir proxy aracılığıyla eriştiğini düşünelim. Hizmet bir başka SM(Sanal Makine) üzerinde olabilir ve her hangi bir remoting teknolojisi ile erişilebilir; bunları istemciden saklamak framework ün var olma nedeni. Framework ümüzün özelliklerinden bir tanesi bir geliştiricinin istemciden, istemciye saydam olarak çağırılan hizmete istediği her hangi bir bağlamı hazırlayabilme yeteneğidir. Örneğin, bir uygulama bir kullanıcıyı güvenlik hizmeti içerisine loglayabilir ve bağlam içine bir yetki kontrolü söz dizimi koyabilir. Bundan sonra, bu
m
uygulama tarafında çağrılan her hizmet yetki kontrolü söz dizimini bağlamdan – sunucu tarafında – alabilir ve onu istemcinin ulaştığı işlevselliği kontrol etmek için kullanabilir. Öncelikle bağlamın hazırlandığını göstermek için basit bir test yazalım:
i.c o
public class BağlamGeçişTest extends TestCase { public void test() { İstemciTaraflıBağlam.varlık().yerleştir(“kullanıcıID”, ”ali”); BağlamAlıcı proxy = (BağlamAlıcı) ProxyFabrikası.varlık().proxyAl(“BağlamAlıcı”); assertEquals(“ali”, proxy.al(“kullanıcıID”)); } }
dil
Testimizde öncelikle bağlama bir yetki kontrolü söz dizimi koyuyoruz. Sonra, hizmetimize bir singleton proxy fabrikasından proxy alıyoruz. (Bu Service Locator örüntüsünün bir örneğidir, burada fabrika uzak bir hizmet için proxy inşa edilmesinin karmaşıklığını istemciden saklıyor.) Hizmet, BağlamAlıcı nın bir varlığı, basit olarak istenen değeri kendi bağlamından döndürüyor. Testin son satırında yetki kontrolü söz diziminin olması gereken değerde olup olmadığını kontrol ediyoruz.
va
Bu örnekle ilgili birkaç yorum yapacak olursak. Birincisi, bu test epeyce anlamsız görünmesine rağmen gerçek bir uygulamada biz bağlamı onu yazdığımız yerden farklı bir yerden okuruz ve aslında bağlam bilgisini geri göndermek yerine onu sunucu tarafında kullanırız.
ww w. ja
İkincisi, bu örnek bir çok yerde singleton örüntüsü kullanılmasına rağmen, eğer biz geniş olarak kullanılan bir framework yazıyorsak kesinlikle onun API lerinde singleton kullanmamalıyız. Singleton somut bir sınıf olmak için tek bir varlığının olmasına gereksinim duyar, oysa sınıfların gerçekleştirimlerini istemciyi etkilemeden değiştirebilmek için sınıflar daima ara yüzler arkasında saklanmalıdırlar. Ayrıca eğer singleton referansı herkese açıksa (global) gerekli olduğu zaman, test esnasında mesela, farklı bir varlığı kullanan bir nesneyi yapmak daha da zordur.
Şimdi testimizin kullandığı sınıflara bakalım. İstemciTaraflıBağlam bir HashMap çevresinde basit bir singleton sarmalayıcıdır, yani bağlamın ona ihtiyaç oluncaya kadar saklandığı yerdir:
public class İstemciTaraflıBağlam { private static final İstemciTaraflıBağlam VARLIK = new İstemciTaraflıBağlam(); pulic static İstemciTaraflıBağlam varlık() { return VARLIK; } private final Map mBağlam = new HashMap(); public void yerleştir (Object anahtar, Object değer) {
public Map bağlamAl() { return mBağlam; }
i.c o
}
mBağlam.put(anahtar, değer);
m
}
BağlamAlıcı ara yüzü sadece aşağıdaki BağlamAlıcı gerçekleştiriminin bir varlığını oluşturur ve döndürür:
dil
public class BağlamAlıcıGerc implements BağlamAlıcı { public Object al (Object anahtar) { return SunucuTaraflıBağlam.varlık().al(anahtar); } } BağlamAlıcıGerc SunucuTaraflıBağlam ın tek bir varlığını döndürüyor, bu İstemciTaraflıBağlam ın yaptığına benzer fakat sunucu tarafında kullanılıyor:
va
public class SunucuTaraflıBağlam { private static final SunucuTaraflıBağlam VARLIK = new SunucuTaraflıBağlam(); public static SunucuTaraflıBağlam varlık() { return VARLIK; }
ww w. ja
private final Map mBağlam = new HashMap(); public void bağlamKur(Map bağlam) { mBağlam.putAll(bağlam); } public Object al(Object anahtar) { return mBağlam.get(anahtar); }
}
Peki bağlam istemciden sunucuya nasıl alınacak? Aşağıdaki aspect ile:
Aspect BağlamGeçirici { before(): execution(* BağlamAlıcıGerc.*(..)) { SunucuTaraflıBağlam.varlık().bağlamKur(İstemciTaraflıBağlam. varlık.bağlamAl()); } }
i.c o
m
Bu aspect tek bir parça advice içeriyor. Bu advice yöntem adviced olmadan önce çağırılan before() advice dır. execution() ifadesi kendilerinden önce advice ın çalıştırılacağı yöntemleri belirliyor. Bu durumda BağlamAlıcıGerc.*(..) ifadesi, poincut olarak ifade ediliyor, BağlamAlıcıGerc sınıfının her yönteminden önce advice ın çalışmasına neden oluyor. Asıl işin yapıldığı advice ın gövde kısmında bağlamın nasıl geçirildiğini görüyoruz: var olan bağlam İstemciTaraflıBağlam dan SunucuTaraflıBağlam kopyalanıyor. Elbette gerçek bir framework de sunucu tarafı farklı bir sanal makine de olacak ve yapmamız gereken biraz daha fazla iş olacak.
Bir AOP istek listesi
dil
Bağlam-geçirme işlevselliğini bir aspect de yazmak bize geleneksel nesneye-dayalı tasarımın üstünde bazı hoş avantajlar verdi. Bir ara yüzü gerçekleştirmek için bir hizmete gereksinim duymadan (Bir EJB(Enterprise JavaBean) bileşenin bir SessionContext almak için yapmak zorunda olduğu) istemci ve sunucu tarafının bağlam-ilişkili sınıflara olan bağımlılığını azaltıyor. Aslında biz bağlam geçişini framework ün kalan kısmından ayırmış olduk, böylece eğer ileride bağlama ihtiyacımız olmaz ise o zaman sadece uygulamamızı BağlamGeçirici aspect i olmadan derlememiz yeterli olacaktır. Yukarıdaki test başarısız olacaktır ama her şey derlenebilecektir ve bağlam geçişine gereksinim duymayan kod normal biçimde çalışacaktır. Bu tam olarak AOP nin sağlamaya çalıştığı modularizasyonun sıralanışıdır.
va
AspectJ AOP gerçekleştirimi ne kadar güçlü olursa olsun geliştiriciler daima isteyecek yeni şeyler bulabilirler. Bunlardan en kesin olanı şuan önümüzde; aspect ler Java da yazılamıyor. Yani sadece yeni bir tasarım öğrenmiyoruz ayrıca yeni bir dil söz dizimi öğreniyoruz ve geliştriciler sadece AspectJ anlamak zorunda kalmıyorlar ayrıca kullandıkları araçlarıda anlamak zorunda kalıyorlar. Aspect lerin tamamen Java ile yazılabilmesi çok hoş olacak.
ww w. ja
AspectJ’in advice ları yöntemlere bağladığı yol – advice edilecek yöntemlerin adları ile eşleşen bir pointcut ifadesi yazmak—geleneksel Java programlarının anlamını başka bir yola çeviriyor. Sıradan Javada, bir yöntem ismi basit bir tanımlayıcıdır, kullanılan yöntemin amacını okuyucuya iyi bir şekilde sunmanın en iyi yolu iyi bir isim seçmektir. Fakat AspectJ de yöntem isimleri (ve sınıf, yapıcı ve alan isimleri)’nin iki amacı vardır: iletişim ve eşleşen ifadelerin hedefi olarak hizmet verme. Aspect ler içeren bir program da bir yöntemin ismini değiştirmek yöntemin edilmesi gerektiği halde advice edilmemesine veya bunun tam tersine sebep olabilir. Yöntem ekleyip çıkarmak da aspect lerle ilgili istenmeyen durumlara sebep olabilir. Bazı araçlar AspectJ ile ilgili bu sorunları ele ala biliyor fakat aspect ler hayatımızı umut ettiğimiz kadar basit yapmıyor. Ve eğer pointcut lar ile tanımlayıcılarımızı uyumlu tutmak istiyorsak bazı tanımlayıcılarımızı değiştirmek zorunda kalırız veya daha kısa pointcut lar yazarız, buda programımızın okunurluğundan ödün verebilir. Başka bir yol varmı?
Dağıtık, çok kullanıcılı sistemler üzerinde olan bizim küçük örneğimizden daha karmaşık uygulamaları düşündüğümüz zaman farklı bir sorun daha ortaya çıkıyor. Bizim örneğimizdeki advice sanal makinede BağlamAlıcıGerc in her varlığına uygulanıyormuş gibi görünüyor. Bunun yerine biz BağlamAlıcıGerc in advice ların uygulandığı ve uygulanmadığı varlıklarını isteyebilmeliyiz. Örneğin bir nesne ile ilişkilendirilen bir advice belleği ve diğer kaynaları aşrı biçimde tüketiyor o zaman muhtemelen ön bellekte duran ve kullanılmayan varlıklara bu advice ın uygulanmasını istemeyiz. Bayt dizileri haline çevirilemeyen (unserializable) nesnelere referanslar içeren advice normalde bayt dizileri haline çevirilebilen bir nesneyi çeviremememize neden olabilir. AspectJ şuanda bir sınıfın sadece bazı varlıklarını advice edebilmemize izin vermiyor. Bu çerçevece yapılan çalışmalar var ama sorun direk olarak ele alınsa daha hoş olacak.
i.c o
m
Son olarak başka bir esneklik istiyoruz: derlenmiş bir programa aspect ler ekleyebilmekten yada aspect ler cıkarabilmekten veya programın yapısını tekrar derleme gerektirmeden aspect eklenebilecek şekilde değiştirmekten hoşlanabiliriz. Bunu AspectJ ile yapamıyoruz çünkü AspectJ advice ları advised sınıflar içerisine derleme zamanında örüyor. Bu davranışlarını yeniden derleme gerektirmeyen konfigürasyonla değiştirmek için çabalayan nesneye yönelik pratiği ihlal ediyor.
dil
Kurumsal yazılımlar dünyasındaki son zamanlardaki eğilim hafif ağırlıklı, saydam orta katmanlara doğru gidiyor. Bir çok geliştirici grubları EJB nin artan karmaşıklığı yüzünden bir çıkış yolu arıyor ve enerjilerini hizmetleri üretmek için yazılmak veya üretilmek zorunda olan boilerplate kod miktarını ve konfigürasyonu azaltmayı amaçlayan framewok ler için harcıyorlar. Bunların açık kaynak dünyasındaki yüksek profilli örnekleri arasında JBoss 4.0, Spring, PicoContainer ve HiveMind var. Büyük uygulama sunucusu üreticileri de bu projeler ile ilgileniyor, BEA’nın Beehive projesine desteği gibi. Bir çok web hizmeti framework ü, bedava veya ticari, EJB-tabanlı bir kab’ a benzeyen veya onun yerine olan basitleştirilmiş bir kab sağlıyor. Ve sonunda Mayıs 2004 de Sundan Linda DeMichiel’in EJB 3.0 ın eski EJB API’sinin çoğunu silip süpürdüğünü açığa vurmasıyla eğilim yeni bir önemlilik düzeyine ulaştı.
va
Bütün bu etkinlikler uygulama sunucusu tasarımındaki araştırmaların çeşitli ilginç alanlarına güç verdi. Burada vurgulamaya çalıştığımız nokta sonunda AOP nin orta katman uygulamalarda çok etkin olduğunun anlaşılmasıdır. Bir çok uygulama sunucusu işlevsellikleri gayet açık ve mantıklı bir biçimde aspect ler olarak ifade edilebilir. Bağlam geçirme, remoting, güvenlik ve hareket gibi işlevsellikler sıradan bir nesnenin yönteminin çağırılmasında yöntem çağrılmasının çevresinde(önce ve/veya sonra) olan işlevsellikler olarak düşünülebilir. Aspect-oriented programlama bir uygulama sunucusu tasarımcısının bu özellikleri soyut sınıflardan kalıtmak veya ara yüzleri gerçekleştirmek için bir hizmet sağlayıcısına gereksinim duymadan sağlayabilmesini sağlar.
ww w. ja
Uygulama sunucusu ortamlarındaki AOP üzerindeki şuandaki ışıldama ile birlikte yukarıda tartışdığımız AspectJ’in kısıtlamaları daha da önemli oluyor. Şuanda ve geçen yıllarda AOP nin gelecek geliştirimler üzerindeki önemi görülünce bu kısıtlamaları ele alan bir çok yeni AOP framework ü geliştiriliyor. Şimdi bağlam geçirme örneğimizin gerçekleştirimini bu yeni framework lerden bir tanesi olan JBoss AOP ile yeniden yapalım ve karmaşık, devingen uygulamaların taleplerine nasıl dayandığını görelim.
JBoss AOP ile Devingen AOP
JBoss AOP JBoss tarafından geliştirilen bir AOP gerçekleştirimidir. JBoss AOP JBoss uygulama sunucusunda AOP kullanmak için gelmekle birlikte AspectJ gibi herhangi bir Java programında kullanılabilen bağımsız bir framework tür. AspectJ ile karşılaştırmasını görmek için daha önce AspectJ ile yaptığımız örneğimize dönelim. Birkaç istenmeyen durumla birlikte AspetJ ile kullandığımız kodun aynısını kullanacağız. Şimdi advice ların JBoss AOP de nasıl olduğunu görelim:
public class BağlamGecici implements Interceptor { public String getName() { return getClass().getName(); } public Object invoke(Invocation invocation) throws Throwable { SunucuTaraflıBağlam.varlık().bağlamKur( İstemciTaraflıBağlam.varlık().bağlamAl());
return invocation.invokeNext();
m
} }
i.c o
JBoss AOP advice org.jboss.aop.Interceptor ara yüzünü gerçekleştiren basit bir Java sınıfıdır. Bu ara yüzde bir tane sıradan yöntem: getName() ve bir tane ilginç yöntem: invoke(Invocation) var. invoke(Invocation) yöntemine AspectJ advice ında koyduğumuz bağlam geçirme kodunun aynısını koyuyoruz. Yöntemin son satırı kontrolü framework e döndürüyor. Burada kötü bir durum var ama farklı bir durumda gerçek yöntem çağrısından dönen değeri başka bir şey ile değiştirebiliriz. Bu kadar, JBoss AOP de advice sadece bir Java sınıfı ve böylece öğrenilecek yeni bir söz dizim yok ve bütün diğer java kodumuzla çalışan geliştirme ortamımızda da çalışıyor. Bu bizim yukarıdaki ilk itirazımızı ele alıyor.
dil
Peki advice ı bir yöntem çağrısına bağlayan AspectJ deki pointcut ların denkliğ nerede? JBoss AOP bunu yapmak için 2 farklı yol öneriyor. Birincisi genellikle jboss-aop.xml diye adlandırılan bir konfigürasyon kütüğü kullanıyor:
va
Bu kütük genellikle sınıfların yüklenme zamanında okunuyor bunun için tekrar derlemeden advice ekleyip silebilir ve advice ın uygulandığı yöntemleri değiştirebiliriz. Bunun yerine eğer istersek AspectJ de yaptığımız gibi aspect lerimizi derleyebiliriz. Bu kütük o zaman yüklenme zamanında değil derleme zamanında yorumlanır.
ww w. ja
Advice larımızı ilişkilendirmenin diğer yolu daha esnek. JBoss AOP ye advice ı hangi yöntemlere uygulamak istediğimizi anlatmak için hala jboss-aop.xml de bir pointcut a ihtiyacımız var:
<prepare expr="execution(* *->@contextual(..))"/> Ama bu hic bir sınıfa advice uygulamıyor, sadece advice ın uygulanacağı sınıfları hazırlıyor. Sınıfımız advice ın uygulanabilmesi için hazırlandı, biz sınıfımıza kendi kodumuz içinde herhangi bir zamanda advice ı uygulayabiliriz.
public class ProxyFactory { private static final ProxyFactory VARLIK = new ProxyFactory(); public static ProxyFactory varlık() { return VARLIK; } public Object getProxy(String ad) {
i.c o
m
Advised advised = (Advised) new BağlamAlıcıGerc(); advised._getInstanceAdvisor().insertInterceptor(new BağlamGecirici()); return advised; } }
AspectJ örneğinde ProxFactory gösterilmemişti çünkü o sadece BaglamAlıcıGerc in bir varlığı olarak inşa edilip döndürülüyordu. Burada biz yeni bir advice oluşturup onu advice ı uygulamak istediğimiz varlıkla ilişkilendiriyoruz. Bu yöntemle advice ı uyguladığımız nesneyi istediğimiz bağlamda kullanmakta tamamem özgürüz. Advice ın uygulandığı ve uygulanmadığı iki ayrı varlığa sahip olabiliriz veya tek bir varlıktan gerekli olduğunda advice ı çıkarabilir veya ekleyebiliriz.
dil
Eşleme ifadeleri kullanarak advice ı nesneler ile ilişkilendirme konusu ilgili olarak nasıl özgür olduğumuz konusunda daha bir şey söylemedik. Ama şu farkedilmiştir ki, jboss-aop.xml kütüğünde kullanılan eşleme ifadelerinde herhangi bir sınıf neya yöntem adından bahsedilmiyor. JBoss AOP ile de AspectJ deki gibi özel sınıflara, yöntemlera vb. hitap eden pointcut ların tanımı yapılabiliyor ama bu örnekte biz bunu kullanmadık. Bunun yerine biz advice ı uygulamak istediğimiz yönteme eklediğimiz bir nota hitap eden @contextual diye bir gösterim kullandık. Bu aşağıdaki BağlamAlıcıGerc in içinde görülüyor:
va
public class BağlamAlıcıGerc implements BağlamAlıcı { /** @@contextual */ public Object al(Object anahtar) { return SunucuTaraflıBağlam.valık().al(anahtar); } }
ww w. ja
JBoss AOP notu iki tane @ işaretli javadoc tag leri gibi gürünüyor. JBoss AOP’nin AnnotationC adlı aracı Java kaynak kodunu ayrıştırıyor ve bulduğu notları genellikle metadata-aop.xml adlı bir kütük içine derliyor. Bu bize en çok esnekliği advice larla kodumuzu ilişkilendirirken verir: AspectJ tarzındaki poincut lar işi yaparken advice ı ilişkilendirildiği kodu değiştirmeden ilişkilendirebiliriz. Eğer bir yöntemler kümesine advice uygulamak istiyorsak bir pointcut yazmak anlamsızdır bunun yerine burada gösterildiği gibi ya hedef yöntemlere notu yerleştiririz veya metadata-aop.xml kütüğünü kendimiz yazarız. J2SE 1.5 (Tiger) ile gelen yenilikleri okuduysak JBoss AOP notlarının J2SE 1.5 notları (JSR 175 de belirtilmiştir) gibi davrandığını farketmişsinizdir. Aslında J2SE 1.5 in son sürümü hazır olduğunda JBoss AOP JSR 175 notlarını i destekleyecektir, kendi notlama sistemini yapması gereksizdir ve ön derleme ihtiyacını ortadan kaldırır.
Yazılımda, başka yerlerde de olduğu gibi, güç ve esneklik bir bedelle gelir. JBoss AOP de de yapıldığı gibi tasarımda bazı sonuclara ulaşmak için vazgeçilen şeyler yeni nesil AOP framework lerinde yapılan tipik bir şeydir. Advice ı çalışma zamanında ilişkilendirmek kod ile AOP framework ü arasındaki ayrımı kırar. Advice hedeflendirmeyi koddan açık olarak ayırmak kodu değiştirmeyi veya ek bir dolaylama katmanının karmaşıklığını gerektirir. Fakat tek iyi iş şudur ki bize ihtiyacımız olanı veriyor ve yeni framework ler üzerinde sarf edilen çaba bu özelliklere uygulama sunucusu dünyasında ihtiyaç duyulduğunu daha açık bir hale getirdi.
m
Rekabet
AOP framework leri
i.c o
JBoss AOP burada örnek olarak verildi ama JBoss AOP tek ciddi ikinci-nesil AOP framework ü değildir. Aşağıdaki tablo Java platformu için mevcut olan diğer AOP gerçekleştirimlerini ve onların özelliklerini listeliyor.
Özellik
AspectJ
AspectWerkz
Örme Zamanı (Weaving time)
Derleme/Yükleme- Derleme/Yükleme- Derleme/Yükleme/Çalışma- Çalışma Çalışma zamanı zamanı zamanı Zamanı Zamanı Saydam
Varlık başına aspect
Hayır
Hayır
Yapıcı, alan, fırlatma ve cflow interception ları
Hepsi
Hepsi
Notlar(Annotations)
Hayır
Tek başına(Standalone)
Evet
AOP Alliance
Hayır
Bağlantı(Affiliation)
IBM
Spring
dynaaop
Seçim
Fabrika
Fabrika
Evet
Evet
Evet
Bazıları
Bazıları
Hiçbiri
Evet
Evet
Evet
Hayır
Evet
Evet
Hayır
Evet
Hayır
Hayır
Evet
Evet
BEA
JBoss
Spring
?
va
dil
Saydamlık(Transparency) Saydam
JBoss AOP
ww w. ja
Framework ler ağırlıklarına göre sıralanmıştır. En soldaki AspectJ kendine ait dil ve destekleyici araçlar, Java kodları ve ve aspect ler arasında katı bir ayırım ve durağan derleme ile en ağır AOP gerçekleştirimi oluyor. Sağdakiler ise istemci koduna daha fazla etki ilede olsa her şeyi çalışma zamanında yapan daha hafif frameworkler. Aslında Java’nın devingen proxylerini de basit bir AOP framework olarak düşünebiliriz bunuda belki tablonun sağ taraflarında bir yerlere yerleştirebiliriz. “Saydamlık” framework ün istemci kodunda değişikliğe ihtiyac duymadanmı (“saydam”) yoksa advice uygulanan nesneleri bir fabrikadan elde etmek için bir istemciye gereksinim duyarakmı (“fabrika”) çalıştığını gösteriyor. JBoss AOP bu iki teknik arasında bir seçim yapabilmemize izin veriyor. “Varlık başına aspect” i destekleyen framework ler advice ın advice uygulanan sınıfın seçtiğimiz bir varlığı ile ilişkilendirilebilmesine izin veriyor. Bu AspectJ ve AspectWerkz’ in yapabildiği farklı aspect kümesi durumlarını advice uygulanan her varlıkla ilişkilendirebilmekten farklıdır. Ayrıca varlık başına aspect kullanmak fabrika kullanmayı gerekli kılar.
“Yapıcı, alan, fırlatma ve cflow interception” framework ün yöntemlerden başka program yapılarına da advice uygulayıp uygulayamadığını gösteriyor. Bu AOP framework lerinin tamamı giriş veya mixin leri destekler, bu burada değinmediğimiz önemli bir AOP özelliğidir. Bir çok AOP framework ü uygulama sunucuları ile ilişkili olsalarda tek başlarınadır. Sadece Spring framework ü hariç veya en azından ayrı olarak mevcut değildir. “AOP Alliance” bazı framework ler tarafından gerçekleştirimi yapılan AOP için genel bir API dir.
Son olarak tablo framework lerle ilişkili olan uygulama sunucusu üreticileri veya geliştiricilerini listeliyor. IBM tarafından kurulan Eclipse Foundation AspectJ i güncelliyor ve IBM den WebSphere e AspectJ desteği eklemesi bekleniyor. İlk önce bağımsız olan
m
AspectWerkz projesi şuanda BEA tarafından destekleniyor ve BEA nın Jrockit sanal makinesini destekliyor. JBoss AOP de JBoss dan geliyor ve JBoss 4.0 da kapsamlı olarak kullanılacak.
i.c o
Bu tablo çok ayrıntılı değil. Daha bir çok AOP gerçekleştirimi ve ilintili projeler mevcut bazıları güçleniyor, bazıları daha yeni duyuruluyor ve bazılarıda yavaş yavaş soluyor.
Buradan nereye gidiyoruz?
AOP için bir çok seçenek var, hangisini kullanmalıyız? Farklı projelerde farklı framework ler uygun oluyor. Daha karmaşık projeler özellikle dağıtık, çok kullanıcılı sistemler muhtemelen daha yeni olan framework lerin devingen özelliklerini kullanmak isteyeceklerdir.
dil
Bu framework lerin çoğu gerçekten tek başlarına olmalarına rağmen, uygulama sunucusu geliştirmek için AOP kullanan bir çok kuruluş kendi uygulama sunucuları ile benzer AOP framework ünü seçiyorlar. AOP’nin Java dünyasındaki geleceği belki uygulama sunusu pazarının geleceğidir. AOP Alliance veya diğer standartlarla AOP framework leri arasında kolay hareket etmenin mümkün olup olmayacağını söylemek için çok erken ama farklı framework lerdeki yaklaşım çeşitliliğine bakılırsa biraz zor görünüyor. Ve AOP orta katmanının dışınıda etkileyebilecekmi? Muhtemelen. Bir çok geliştirici AOP hakkında düşünmeden AOP kullanılan orta katmanlarda çalışacak. Fakat her proje şimdi veya daha sonra mutlaka motorun kapağını açmaya ihtiyaç duyar, AOP’nin orta katmanda neler yapabildiğini görünce muhtemelen bir çoğumuz onu yazılım sistemimizin her yerinde kullanacağız. Orta katmanın her yerde yaygınlaşması AOP için büyük bir şans olabilir.
ww w. ja
va
Bu makale www.javaworld.com adresindeki Dave Schweisguth a ait “Second-generation aspect-oriented programming, Apply advice dynamically with the new crop of AOP frameworks ” adlı makaleden cevrilmiştir.
m
Spring AOP (Aspect Oriented Programming) Gerçekleştirimi
i.c o
Spring’in AOP paketi AOP Alliance uyumlu bir aspect-oriented programming gerçekleştirimi sağlar. Bu paket ile, yöntem-interceptor lar pointcutlar tanımlayarak kodumuzdaki bağımlılıkları rahatlıkla engelleyebiliriz. Kaynak düzeyi metadata işlevselliğini kullanarak bütün davranışsal bilgi türlerini kodumuza dahil edebiliriz. AOP program yapısı hakkında farklı bir düşünme yolu sağlayarak NYP yi tamamlıyor. NYP uygulamaları nesneler hiyerarşisinde parçalarken, AOP programları aspect ler veya ilgiler olarak parçalıyor. Bir ilgi özel bir amaç, kavram veya ilgi alanıdır.
dil
Spring’in anahtar bileşenlerinden biride AOP framework ü. Spring’in IoC (Inversion of Control) kabları (BeanFactory ve ApplicationContext) AOP ye bağlı değildir yani AOP yi kullanmak zorunda değilsiniz istemiyorsanız kullanmazsınız ama AOP Spring’in IoC sini tamamlayarak çok yetenekli bir orta katman çözümünün ortaya çıkmasını sağlar. Amaç en iyi AOP gerçekleştirimini sağlamak değildir (Spring AOP’nin epey yetenekli olmasına rağmen), amaç AOP gerçekleştirimi ile Spring IoC arasında yakın bir bütünleştirme sağlayarak kurumsal uygulamalardaki genel problemleri çözmektir. AOP Spring de aşağıdaki sebeplerden dolayı kullanılır:
va
• Bildirimsel kurumsal hizmetler sağlamak için özellikle EJB bildirimsel hizmetlerin yerine kullanılmak üzere. En önemli hizmet Spring’in hareket soyutlamasını oluşturan bildirimsel hareket yönetimidir. • Kullanıcıların aspect leri gerçekleştirmelerine izin vererek onların NYP ile birlikte AOP yi kullanmalarını tamamlamak.
ww w. ja
Spring’in AOP desteğinin ilk amacı POJO lara J2ee hizmetleri sağlamaktır. Bu JBoss 4 ün amacı ile benzerdir. Ancak Spring AOP’nin uygulama sunucuları arasında taşınabilen avantajları vardır böylece üreticiye dönük bir bağlanma söz konusu değildir. Spring AOP hem web kabında hem de EJB kabında çalışır ve WebLogic, Tomcat, JBoss, Resin, Jetty, Orion ve diğer bir çok uygulama sunucusunda ve web kabında başarı ile kullanılmıştır. Aşağıda bazı AOP kavramlarını anlatmaya çalışacağım ama çoğunun Türkçe karşılıkları hakkında henüz çok fazla bir fikrim yok: • Aspect: İlginin ne olduğunu daha önce açıklamıştım. AOP de modüllere ayırma birimine verilen ad aspect tir, bu genel bir ilginin gerçekleştiriminin NYP de sınıf adını almasına benzer. Aspectlerin gerçekleştirimi Spring kullanılarak Advisor veya interceptor lar olarak yapılır. • Birleşme noktası (Joinpoint): Programın çalıştığı süreç boyunca yöntemlerin çalıştırıldığı veya özel istenmeyen durumların fırlatıldığı noktalar. Spring AOP de bir birleşme noktası her zaman yöntem çağırmadır. • Advice: AOP framework tarafından özel bir birleşme noktasında yapılan hareket. Advice ların “around”, “before” ve “throws” diye ayrılan türleri vardır. Spring’in de içinde bulunduğu bir çok AOP framework ü advice ı interceptor olarak modeller. • Pointcut: Bir advice’ın uygulanacağı birleşme noktaları kümesi. Bir AOP framework ü kullanıcının örneğin düzenli ifadeler kullanarak pointcut lar belirleyebilmesini sağlamalıdır. Durağan pointcut lar yöntem imzalarıyla ilgilenirler, devingen pointcutlar hesaplandıkları noktada yöntem argümanlarınıda hesaba katabilirler. Pointcutlar
• •
m
•
i.c o
•
interceptor lardan ayrı olarak tanımlanır böylece standart bir interceptor in değişik uygulama ve kod bağlamlarına uygulanabilmesi sağlanır. Giriş: Advice’ın uygulandığı bir sınıfa yöntemler veya alanlar eklemek. Spring advice ların uygulandığı nesnelere yeni ara yüzler tanıtabilmemize izin verir. Hedef Nesne: Birleşme noktası içeren nesne ayrıca advised veya proxied nesne olarak da söylenebilir ben bunları advised nesneyi advice (lar)ın uygulandığı nesne olarak çevirdim. AOP Proxy: Advice içeren AOP framework tarafından yaratılmış nesne. Spring de AOP proxy bir JDK devingen proxy veya CGLIB proxy olabilir. Örme(Weaving): Advice (lar) ın uygulandığı nesneyi yaratabilmek için aspect leri birleştirme. Bu işlem derleme zamanında yapılabilir (örneğin AspectJ derleyicisi kullanılarak) veya çalışma zamanında yapılır. Spring diğer saf java olan AOP framework leri gibi örme işlemini çalışma zamanında yapar.
dil
Spring durumsal(stateful) (her advised nesne için bir varlık) ve durumsuz(stateless) (bütün advice lar için bir valık) interceptor ların her ikisini de destekler. Spring AOP yi devingen proxy ler veya çalışma zamanında CGLIB bayt kod üretimi kullanarak gerçekleştirir. Her iki yaklaşımda bütün uygulama sunucularında çalışır. Muhtemelen Spring AOP nin en çok kullanıldığı alan bildirimsel hareket yönetimidir. Bu yapı TransactionTemplate soyutlaması üzerine kurulmuştur ve herhangi bir POJO üzerinde bildirimsel hareket yönetimi sağlayabilir. Hareket stratejisine bağlı olarak kullanılan mekanizma JTA, JDBC, Hibernate veya hareket yönetimi sağlayan herhangi başka bir API olabilir.
va
Spring’in bildirimsel hareket yönetimi aşağıdaki farklarla EJB CMT ‘ e benzer.
ww w. ja
• Hareket yönetimi herhangi bir POJO ya uygulanabilir. Spring iş nesnelerinin ara yüzleri gerçekleştirmesini tavsiye eder ama bu sadece iyi bir programlama pratiğidir ve framework tarafından zorlanmaz. • Spring hareket API si kullanılarak hareketsel bir POJO içinde programsal geri sarmalar (programmatic rollback) yapılabilir. Spring bunun için ThreadLocal değişkenlerini kullanarak statik yöntemler sağlar böylece geri sarma garanti etmek için EJB Context gibi bağlam nesneleri hazırlamak zorunda kalmayız. • Geri Sarma kuralları tanımlanabilir. EJB uygulamada oluşan ve yakalanamayan bir istenmeyen durumda otomatik olarak hareketin geri alamaz ve uygulama geliştiriciler genellikle her istenmeyen durumda hareketi geri almak isterler. Spring hareket yönetimi hangi istenmeyen durumların ve alt sınıfların otomatik geri sarmaya neden olması gerektiğini belirlememizi sağlar. • Hareket yönetimi JTA ya bağımlı değildir, Spring hareket yönetimi değişik hareket stratejileri ile çalışabilir.
Uygulamaya özel aspect lerin gerçekleştirimini de Spring AOP kullanarak yapabiliriz. Fakat bu Spring’in bu alandaki yeteneklerinden daha çok bizim AOP kavramlarına yakınlığımıza bağlı. Bu konuda aşağıdaki gibi başarılı örnekler mevcuttur: • Özel güvenlik interception ı. • Geliştirme sürecinde kullanmak için debug ve profil aspectleri. • Beklenmeyen durumlarda yöneticilere veya kullanıcılara e-posta atan interceptorlar.
Spring AOP Spring BeanFactory kavramı ile saydam olarak bütünleşir. Spring BeanFactory den nesne elde etmeye yarayan kod kendisine advice uygulanıp uygulanmadığını bilmek zorunda değildir.
Spring AOP’nin Kullanılışı
i.c o
m
Burada Spring AOP nin kullanılışı ile ilgili bazı örnekler anlatılacaktır. Bunlar tracing ve logging aspect örnekleri, aspect oriented’ın Hello World’ü, Spring framework’ün sağladığı yetenekleri kullanarak aspect leri uygulamak amacıyla pointcut ve advice lar tanımlama ve Spring de gerçekleştirimi yapılan aşağıdaki AOP kavramlarının nasıl kullanıldığıdır. • Advice: before, afterReturning ve afterThrowing advice ları bean ler olarak nasıl tanımlanır. • Pointcuts: her şeyi XML Spring Bean konfigürasyon kütüğünde beraber bağlayabilmek için durağan pointcut mantığı nasıl tanımlanır. • Advisors: pointcut tanımlarını advice beanleri ile ilişkilendirmek için bir yol. İlk adımda basit bir java uygulaması oluşturacağız. IIsMantigi ara yüzü ve onu gerçekleştiren bir IsMantigi sınıfı oluşturacağız.
dil
Public interface IIsMantigi{ public void is(); }
va
public class IsMantigi implements IIsMantigi { public void is () { System.out.println(“IsMantigi.is()’in icindeyiz”); } } Ve IsMantigi bean’inin genel yöntemini kullanacak bir AnaUygulama yazılabilir. import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;
ww w. ja
public class AnaUygulama { public static void main (String[] args) { //konfigürasyon kütügünü oku ApplicationContext ctx = new FileSystemXmlApplicationContext(“springconfig.xml”); //bir nesne al IIsMantigi testNesne = (IIsMantigi) ctx.getBean(“ismantigibean”); // public yöntemi calistir. testNesne.is(); } } Spring konfigürasyon bilgileri aşağıdaki gibi bir XML kütüğünde sağlanır.
m
dil
i.c o
<property name="proxyInterfaces"> IIsMantigi <property name="target">
Bu konfigürasyon kütüğü ,“springconfig.xml” olarak kaydedildi, yüklenecek bean’in ara yüzünü IIsMantigi olarak eşlendi. Sonrada bean IsMantigi gerçekleştirim sınıfına bağlandı.
Spring.jar aopalliance.jar commons-logging.jar jakarta-oro.jar
ww w. ja
• • • •
va
Aşağıdaki şekilde AnaUygulama çalıştığı zamanki ardıl etkileşim çizeneği gösteriliyor. Bu uygulamayı çalıştırabilmek için öncelikle yol (path) ayarları yapmamız gerekecek başta Spring olmak üzere birkaç tane kütüphaneye ihtiyacımız olacak bunlar aşağıdaki .jar uzantılı kütüklerdir:
nesnem:Sinifim
ctx:ApplicationContext
getBean(String)
create
testNesne:IsMantigi
void is()
Şekil 4 - Ardıl Etkileşim Çizeneği IsMantigi beanini hiçbir aspect uygulanmadığı zaman gösteriyor
i.c o
m
En temel aspect muhtemelen Method Tracing aspect tir, bulabileceğimiz en basit aspect olduğu için yeni bir AOP yi incelerken başlanması gereken muhtemel en iyi yerdir. Bir Method Tracing aspect hedef bir uygulama içinde izlenilen yöntemleri çağırmayı ve yöntemlerden dönmeyi ele alır ve elde ettiği bilgiyi bir yolla gösterir. Bu türdeki birleşme noktalarını (join points) ele almak için before ve after advice türleri kullanılır. Bu advice lar yöntemlerin bu birleşme noktalarından önce veya sonra tetiklenirler. Spring framework kullanılarak Method Tracing aspect için before advice TracingBeforeAdvice sınıfında tanımlanmıştır.
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice;
dil
public class TracingBeforeAdvice implements MethodBeforeAdvice { public void before (Method m, Object[] args, Object target) throws Throwable { System.out.println(“Hello world! (by ” + this.getClass().getName() + “)”); } } Benzer biçimde after advice ı TracingAfterAdvice sınıfında tanımlanabilir.
va
import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class TracingAfterAdvice implements AfterReturningAdvice {
ww w. ja
public void afterReturning(Object object, Method m, Object[] args,Object target) throws Throwable { System.out.println("Hello world! (by " + this.getClass().getName() + ")"); } }
Her iki sınıfta Spring framework ünden uygun advice ara yüzlerinin gerçekleştirimini yaparak advice ın özel bir parçasını temsil ediyor. Her iki advice türü de Spring’in çalışma zamanında uygun birleşme noktalarına ulaşıldığında advice ları bildirebilmesini etkin kılmak için hem before(..) hemde afterReturning(..) yöntemlerinin gerçekleştiriminin yapıldığını belirtir. TracingAfterAdvice sınıfının AfterReturningAdvice sınıfının gerçekleştirimini yapması aslında hiçbir şey ifade etmez bunun anlamı advice ın sadece birleşme noktasına istenmeyen durum oluşmadan ulaşılırsa çalışabileceğidir. Advice ları uygulamamızda uygun birleşme noktaları ile ilişkilendirebilmek için springconfig.xml birkaç ekleme yapmak zorundayız. Yeni eklenen kısımlar koyu renkte görünüyor.
m
"http://www.springframework.org/dtd/spring-beans.dtd">
dil
i.c o
<property name="proxyInterfaces"> IIsMantigi <property name="target"> <property name="interceptorNames"> <list> theTracingBeforeAdvisor theTracingAfterAdvisor
va
ww w. ja
class="org.springframework.aop.support.RegexpMethodPointcutAdviso r"> <property name="advice"> <property name="pattern"> .* <property name="advice"> <property name="pattern"> .*
i.c o
m
theTracingBeforeAdvisor ve theTracingAfterAdvisor advisor ları daha önce tanımlanan ismantigibean’ine eklendi. Her advisor bean üzerinde ilişkilendirildikleri
birleşme noktalarını kesecektir. Advisor lar bean lerin kendileridir ve bunların işi poincut tanımlarını ve advice beanlerini bir arada bağlamaktır. Bu örnekteki pointcut tanımları düzgün ifadelerdir. Bu ifadeler genel ara yüz IIsMantigi ara yüzündeki birleşme noktalarını belirlemek için kullanılmıştır. IIsMantigi ara yüzü üzerinde farklı birleşme noktaları kümesi tanımlamak için kullanılabilecek bazı düzgün ifade örnekleri aşağıdaki gibidir.
dil
•
.*: Bu ifade advisor ın ilişkilendirildiği beanler veya bean üzerindeki bütün birleşme noktalarını seçer. •
./IIsMantigi/.is: Bu ifade IIsMantigi ara yüzü üzerindeki is()yöntemi üzerindeki birleşme noktalarını seçer.
nesnem:Sinifim
va
Bu konfigürasyon ile AnaUygulama yı çalıştırırsak ardıl etkileşim çizeneğimiz aşağıdaki gibi olur.
ctx:ApplicationContext
getBean(String)
ww w. ja
create
advice1 : T racingBeforeAdvice
advice2 : T racingAfterAdvice
testNesne:IsMantigi
void before(Method, Object[], Object)
void is()
void afterReturning ( Object, Method, Object[], Object)
Şekil 5 - Ardıl Etkileşim çizeneği IsMantigi bean ine uygulanan Method Tracing aspect’i gösteriyor.
AnaUygulama sınıfının çalıştırdığımız zaman ekrandan alacağımız çıktı aşağıdaki gibi olacaktır.
Hello world! (by TracingBeforeAdvice)
m
IsMantigi icindeyiz. Hello world! (by TracingAfterAdvice)
i.c o
Method Tracing aspect daha karmaşık olan Logging aspect elde etmek için genişletilebilir. Logging aspect tekrar kullanmaya iyi bir örnektir. Uygulamamız içinde Logging aspect i kullanabilmek için birkaç değişiklik yapmak gerekiyor ve bir istenmeyen durum sınıfı oluşturuyoruz. IsMantigiIstenmeyenDurum sınıfı IIsMantigi ara yüzündeki yeni method void is2() den ve IsMantigi gerçekleştirim sınıfından fırlatabileceğimiz bir istenmeyen durum sağlıyor.
public class IsMantigiIstenmeyenDurum extends Exception { }
dil
public interface IIsMantigi { public void is();
public void is2() throws IsMantigiIstenmeyenDurum;
va
}
ww w. ja
public class IsMantigi implements IIsMantigi { public void is() { System.out.println( "IsMantigi.is() in icinde"); }
public void is2() throws IsMantigiIstenmeyenDurum { System.out.println( "IsMantigi.is2() nin icinde"); throw new IsMantigiIstenmeyenDurum(); }
}
AnaUygulama sınıfı da void is2() yöntemini de çağırıyor ve potansiyel olarak bu sınıftan fırlatılan istenmeyen durumları ele alıyor. import org.springframeworkcontext.ApplicationContext;
m
import org.springframework.context.support.FileSystemXmlApplicationConte xt;
i.c o
public class AnaUygulama { public static void main(String [] args) { // Read the configuration file ApplicationContext ctx = new FileSystemXmlApplicationContext( "springconfig.xml");
dil
//bir nesne olustur IIsMantigi testNesne = (IIsMantigi) ctx.getBean( "ismantigibean");
try {
va
//bean’in genel yöntemlerini çalıştır. testNesne.is();
ww w. ja
testNesne.is2(); } catch(IsMantigiIstenmeyenDurum imid) { System.out.println( "IsMantigiIstenmeyenDurum yakalandi"); }
}
}
LoggingThrowsAdvice sınıfı yeni istenmeyen durum loglaması için advice sağlar.
import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; public class LoggingThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, Throwable subclass) { System.out.println( "Logging that a " + subclass + "Exception was thrown.");
}
m
}
Son adım olarak springconfig.xml konfigürasyon kütüğünü değiştirmek kalıyor. AnaUygulama sınıfını çalıştırınca alacağımız çıktı aşağıdaki gibi olacaktır:
i.c o
Spring loglama bilgileri….. Hello world! (by TracingBeforeAdvice) IsMantigi icindeyiz. Hello world! (by TracingAfterAdvice) Spring loglama bilgileri… Hello world! (by TracingBeforeAdvice) IsMantigi.is2() nin icinde Spring loglama bilgileri… Logging that a IsMantigiIstenmeyenDurumException was thrown. IsMantigiIstenmeyenDurum yakalandi
nesnem:Sinifim
ctx:ApplicationContext
getBean(String)
advice1 : T racingBeforeAdvice
advice2 : T racingAfterAdvice
advice3 : LoggingT hrowsAdvice
testNesne:IsMantigi
va
create
dil
Ve ardıl etkileşim çizeneğimiz de aşağıdaki gibi olacaktır:
void before(Method, Object[], Object)
void is()
ww w. ja
void afterReturning ( Object, Method, Object[], Object) void before(Method, Object[], Object)
void is2()
void afterT hrowing(Method, Obect[], Object, T hrowable)
Şekil 6 - Logging advice ının IsMantigi bean ine uygulanmasını gösteren ardıl etkileşim çizeneği.
Logging aspect etkili bir biçimde var olan aspect leri parçalarını nasıl yeniden kullanabileceğimizi ve advice ın throws formumun Spring framework de nasıl kullanılacağını gösteriyor. Burada Spring AOP nin basit bir biçimde nasıl kullanılabileceğini anlatmaya çalıştım.Bu makale ve ilgili örnekler www.onjava.com sitesinde ki Russell Miles a ait ‘An Introduction to Aspect-Oriented Programming with Spring Framework, Part 1’ adlı makaleden çevrilmiştir.
www.javaworld.com www.onjava.com
ww w. ja
va
dil
i.c o
• •
m
KAYNAKÇA