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.