V poslední době jsem se začal věnovat aspect oriented programming (AOP) a aspectj. Velmi mě překvapilo jak elegantně se pomocí AOP dá řešit určité problémy a měření výkonu a času strávené voláním metod patří do této kategorie. Před tím, než budete pokračovat v čtení doporučuji začít krátkým tutoriálem: http://www.eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html, kde jsou vysvětleny základní pojmy. Koho by tato oblast zajímala více vřele doporučuju http://www.manning.com/laddad/. Pro ty netrpělivý jsem se pokusil shrnout nejduležitějsí informace.

AOP jako technologie nepřichází aby nahradila stávající technologie, ale je to doplňek, který se hodí na určité problémy. I sami autoři připouští, že AOP se na projektu dá využit z 15% a zbytek bude standartní kód. Fungování AOP nejlépe vystihuje princip proxy, kdy se stávající funkčnost obalí novou funkčnosti přidanou AOP. Například implementace AOP ve springu je založena na proxy třídách http://static.springsource.org/spring/docs/2.5.x/reference/aop.html. Základy AOP mají pěknou analogii s databází:

AOP

Databáze

Advice

Trigger

Pointcut

SQL

Tak jak se používá trigger v databázích k vykonání dodatečné funkčnosti při práci s daty, to samé platí i v AOP pro advice, který přidává do stávající funkčnosti něco extra. Základní typy advice jsou before, after a around jak jejich názvy napovídají definují kdy se advice vykoná.

Na druhou stranu pointcut vybírá místa na které se bude aplikovat advice. Může se jednat o volání metod, vytváření objektu, modifikace proměné apod. Důležité je, že pointcut je určitého typu, tím myslíme, že pointcut vybírající metody nemůže být použit v kombinaci s pointcut vybírající read/write access k proměné objektu apod.. Nejzákladnější typy pointcut jsou:

  • execution: může být použit na vykonání metod a konstruktorů. Pokud daná metoda spadne do výběru „obalí“ se tělo metody/konstruktoru.

  • call: Aplikuje se na volání metod a kontruktů rozdíl oproti execution je, že se „obaluje“ kód, kde je daná metoda/konstruktor zavolán.

Dále existují pointcut, které nemají žádny typ tj. lze je použít kdekoliv. Jsou to speciální typy níže v přikladu uvidíte jejich použití jedná se např. o pointcuty:

  • within: umožnuje vybírat kód, který je například definován package či konkrétním typem. within(cz.thomola..*)

  • this: Umožnuje sbírat kontext infomace tak aby se v nich dalo dále v advice pracovat. this(type) vybere kód, kde platí this instanceof type

Pointcuty je možno kombinovat pomocí binárních operátoru && a || jejich význam je stejný jako v javě. Tím můžeme vytvářet složitější pointcuty kombinaci jednoduších. Pointcut je možné negovat pomocí !.

Poslední věc o které bych se rád zmínil je weaving. Aspekty by sami o sobě neměli význam a weaving definuje jak dojde ke spojení aspektů a tříd. Existují dva druhy weavingu.

  • Build time má velkou výhodu, že kontrola funguje již při buildu aplikace, zároveň ale je ale nutno upravit stávající build process. Na druhou stranu není to nic extrémně složitého a při použití ant či maven to znamená malou úpravu.

  • load time weaving probíhá při načítání třídy do VM. Load time weaving využívá vlastnosti, která přišla s Java verze 5. Je to možnost vložit agenta mezi, který může ovlivnit nahrání třídy do VM.

Tímto bych uzavřel teorii, která i tak obsahuje to nejmenší minimum. Podívejme se na to jak si udělat jednoduchý performance monitor. Jako první věc bych ukázal jednoduchý výstup, který ukazuje čas strávený v metodě a zanoření volání metod ve stacku, což může být zajímavá informace z produkčního prostředí při provádění optimalizace.

 

PerformanceTest.testMethod(argument) - 43
PerformanceTest.testMethod2() - 25
PerformanceTest.testMethod3(testMethod2) - 24
PerformanceTest.testMethod4(testMethod2,xxx) - 24
PerformanceTest.testMethod3(dalsi) - 0
PerformanceTest.testMethod4(dalsi,xxx) - 0
PerformanceTest.testMethod2() - 1

 

Základem je abstraktní aspect, který, definuje abstract pointcut toMeasure(), kde je na potomkovy aby definoval jaké metody bude chtít do monitoringu zahrnout. Advice before, after slouží jako dispatcher na třídu, která implementuje vlastní logiku zpracování. Tím se taky dostanem k hlavní myšlence aspektů, které by měli sloužit jako dispatcher.

 

public abstract aspect PerformanceMonitorAspect {
private IPerformanceMonitor monitor = new PerformanceMonitor();

public abstract pointcut toMeasure();
private pointcut measurePointcut(Object obj) : toMeasure() && this(obj) && !within(cz.thomola.aop..*);

before(Object obj) : measurePointcut(obj) {
String objectName = obj != null ? obj.getClass().getSimpleName() : "";
String methodName = thisJoinPointStaticPart.getSignature().getName();
Object[] args = thisJoinPoint.getArgs();
this.monitor.startMethodMeasurement(objectName, methodName,args, System.currentTimeMillis());
}
after(Object obj) : measurePointcut(obj) {
String objectName = obj != null ? obj.getClass().getSimpleName() : "";
String methodName = thisJoinPointStaticPart.getSignature().getName();
Object[] args = thisJoinPoint.getArgs();
this.monitor.endMethodMeasurement(objectName, methodName, args, System.currentTimeMillis());
if (this.monitor.isReadyForExport()) {
doExportCollectedInformation(methodName)
}
}
private void doExportCollectedInformation(String method) {
this.monitor.exportData(new File(method+".txt"));
}

}


Zajímavým řádkem je u definice pointcutu measurePointcut()

!within(cz.thomola.aop..*);

 

Tímto z výběru odstranímě volání všech metod, které mají jakékoliv spojení s aspekty. A také se tím vyhneme problémů s nekonečným cyklem. Představme si, že bychom v before advice volali metodu, která by spadala do výběru. Takže výsledkem by bylo zacyklení. To by vypadalo asi takto: before advice → metoda → before advice → metoda atd. Proto se doporučuje z jakéhokoliv výběru odstranit vše spojené s aspekty pomoci předefinovaného pointcutu within(pattern);

Vraťmě se z odbočky. Implementace pointcutu na potomkovy může vypadat například takto:

public pointcut toMeasure() :  execution(public * cz.thomola.test..*(..))

 

Význam pointcutu je jednoduchý, vybere vykonání public metody, jejichž návratová hodnota nás nezajímá. Metoda je na objektu v package cz.thomola.test a jeho subpackage, kde název metody může být libovolný a metoda má 0..n parametrů.

Jak naložíme s informacema, které aspekt vybere, záleží na naší implementaci, může to být jednoduchý textový výstup viz. Výše, můžeme to vyexportovat do HTML, cokoliv to záleží na nás.

Přikládám testovací projekt, jeho jediný problém je, že je pro eclipse, kde existuje plugin pro podporu aspectj http://www.eclipse.org/ajdt/. Pro netbeans jsem zatím nic nenašel jsou zde určité pokusy http://jroller.com/ramlog/entry/using_the_aspectj_plug_in1 a http://sourceforge.net/projects/aspectj-for-nb6/files/. Pokud nainstalujete do eclipse ajdt plugin stačí projekt naimportovat do worskpace pomocí file ->import ->existing projekt into workspace a vše by mělo fungovat bez dalšího nastavování.

Doufám, že Vás zaujala elegance a jednoduchost jak jsme získaly cenné informace a propadnete kouzlu AOP jako já a budete AOP dále zkoumat. Tím bych také uvítal nápady na další článek jakou problematikou se zabývat.