AspectJ SummerCamp Arno Schmidmeier JUG Stuttgart 16./17.7.2003 Inhalt • Motivation für AOP • Lösungsansatz mit Java-Bordmitteln oder wie kann ich nur mit Java AOP artig programmieren • Dynamic crosscutting mit AspectJ • Static crosscutting mit AspectJ • Dazwischen Idiome, Patterns und Beispiele © Arno Schmidmeier 2003 Dynamic crosscutting • • • • • • • • Advice Primitive pointcuts Joinpoints thisJoinPoint Exception Handling Aspekt Class Instanzierung Aspect Inheritance Abstrakte Aspect © Arno Schmidmeier 2003 Static Crosscutting • • • • • Declare error / declare warning Softening von Exception Introduction (aka Open Classes) Verändern der Vererbungshierarchie Declare precedence (ab 1.1) © Arno Schmidmeier 2003 Motivation Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Ziel der Softwareengeneering seit über 30 Jahren • “Divide and conquer” • Stichwort: “Separation of Concerns” • Jeder Concern soll in einem eigenem Modul, Klasse Funktion, etc. gekapselt werden © Arno Schmidmeier 2003 Was ist ein Concern? • Ein bestimmtes Ziel, Konzept oder Interessensgebiet • Ein Softwaresystem besteht aus: – Fachlichen Concerns (Geschäftslogik) – Nicht Fachlichen Concerns © Arno Schmidmeier 2003 Das Bankkonto-Beispiel Fachliche Concerns: – Überweisung, Einzahlung, Abhebung, ... – Gesperrtes Konto – Bonitätsprüfung, Kundenstatus, etc. Nicht fachliche Concerns: –Logging –Transaction integrity –Authorisation –Security –Performance, etc. © Arno Schmidmeier 2003 Klasse Konto Konto Ueberweisen(Betrag,Konto) Abheben(Betrag) Einzahlen(Betrag) Kontonummer Guthaben • Einfache Klasse • „Drei“ fachliche Methoden überweisen, abheben einzahlen • Attribute Kontonummer, Guthaben © Arno Schmidmeier 2003 Im Source-Code public class Konto { String Kontonummer; double Guthaben; public void abheben(float Betrag) { Guthaben=Guthaben-Betrag; } public void einzahlen(float Betrag) { Guthaben=Guthaben+Betrag; } public void ueberweisen(float Betrag,Konto AnderesKonto) { Guthaben=Guthaben-Betrag; AnderesKonto.einzahlen(Betrag); } } } © Arno Schmidmeier 2003 Mit Logging lt e p public void ueberweisen(float Betrag,Konto AnderesKonto)throws NichtMoeglich{ Logger.log("ueberweisen called with"+Betrag+" "+AnderesKonto); if (Betrag<=0){ NichtMoeglich ex=new NichtMoeglich(" Programfehler, negativer Betrag“); Logger.log("Exiting ueberweisen with Exception "+ex); throw ex; } if (AnderesKonto==null){ NichtMoeglich ex=new NichtMoeglich(" Programfehler, Konto == null“); Logger.log("Exiting ueberweisen with Exception "+ex); throw ex; } Guthaben=Guthaben-Betrag; try{ AnderesKonto.einzahlen(Betrag); checkIstMoeglich(); }catch(NichtMoeglich ex){ Logger.log("Exiting ueberweisen with Exception "+ex); throw ex; }catch(RuntimeException ex){ Logger.log("Exiting ueberweisen with Exception "+ex); throw ex; } © Arno Schmidmeier 2003 Logger.log("Exiting ueberweisen normally"); n i L o s e o C f n e d h c o a m e v l p o rd Martin Fowler: Refactoring! } Mit fachlichen Concerns private void inlog_uberweisen(float Betrag,Konto AnderesKonto) throws NichtMoeglich{ if (Betrag<=0) throw new NichtMoeglich(" Programfehler, " ); if (AnderesKonto==null) throw new NichtMoeglich (" Programfehler, " ); Guthaben=Guthaben-Betrag; AnderesKonto.einzahlen(Betrag); checkIstMoeglich(); } 2,5 mal soviel Zeilen, aber immerhin noch halbwegs lesbar © Arno Schmidmeier 2003 Nach dem Refactoring (1) private void real_uberweisen(float Betrag,Konto AnderesKonto)throws NichtMoeglich{ Guthaben=Guthaben-Betrag; AnderesKonto.einzahlen(Betrag); } private void condition_uberweisen(float Betrag,Konto AnderesKonto)throws NichtMoeglich{ if (Betrag<=0) throw new NichtMoeglich(" Programfehler, negativer Betrag"); if (AnderesKonto==null) throw new NichtMoeglich(" Programfehler, Konto==null"); real_uberweisen(Betrag,AnderesKonto); checkIstMoeglich(); © Arno Schmidmeier 2003 } Nach dem Refactoring (2) public void uberweisen(float Betrag, Konto AnderesKonto) throws NichtMoeglich{ Logger.log("ueberweisen called with"+Betrag+" "+AnderesKonto); boolean mustCloseTransaction=TransactionManager.getInstance().start Transaction(); try{ condition_uberweisen(Betrag,AnderesKonto); }catch(NichtMoeglich ex){ Logger.log("Exiting ueberweisen with Exception "+ex); }catch(RuntimeException ex){ Logger.log("Exiting ueberweisen with Exception Idee! "+ex); } Schichtenbildung Logger.log("Exiting ueberweisen normally"); } © Arno Schmidmeier 2003 t h t h ifac c a t f e r s n h erd da c : n r rse v e u b ve en red a , de d st ! o r h i e t o r s e a f C M de b s o o r e e s C L ne l d s Li zah ¾ de An ind. M Lösungsansatz mit JavaBordmitteln Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Schichtenbildung Klasse Konto Pre-Post Conditions Transaction Handling Gener. Tracing Gener. Tracing2 Tracing Tracing2 User Validation © Arno Schmidmeier 2003 LoggingSubsystem Transaction Subsystem Wie Implementieren? Interceptoren! Foo() Foo() Transaction Handling Logging Obj 1 Foo() Obj 2 Foo() © Arno Schmidmeier 2003 In Java: Dynamic Proxies • Seit JDK 1.3 können Interceptoren dynamisch um Funktionen einen Interfaces gewrappt werden • Crosscutting Concerns können damit grundsätzlich getrennt werden • Die Erzeugung kann nur über Factories und andere Erzeugungsmuster erfolgen © Arno Schmidmeier 2003 In Java: Dynamic Proxies • Probleme: – Funktioniert nur mit Interfaces – Die Interceptoren müssen eingefügt werden – Arbeiten mit dem Reflection API, langsam, aufwendig zu programmieren etc. – Proxy-Wrapper Factories und Konfigurationsmechanismen müssen geschrieben und gewartet werden – Der Client muß die Interceptoren kennen © Arno Schmidmeier 2003 Dynamic crosscutting mit AspectJ Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Interceptoren In AspectJ: Advice • Auf dem Hinweg, before dem ursprünglichem Code • Auf dem Rückweg, after dem ursprünglichem Code • Anstatt der eigentlichen Ausführung, around der ursprünglichen Methode © Arno Schmidmeier 2003 Before advice • Aktion wird vor dem ursprünglichem Codestück am Joinpoint ausgeführt. before():execution(* s*.*(..)){ Tracer.trace(thisJoinPoint); } © Arno Schmidmeier 2003 After advice • Aktion wird nach dem ursprünglichem Codestück am Joinpoint ausgeführt after():execution(* s*.*(..)){ Tracer.trace(thisJoinPoint); } © Arno Schmidmeier 2003 Around Advice • Anstelle des Codes führe die Aktion aus Wo und wie wird bestimmt, wo der Advice in den Code kommt Object around():execution(* org..*.*(..)){ Tracer.trace(„before “+thisJoinPoint); Object toreturn=proceed(); Tracer.trace(„after “+thisJoinPoint); return toreturn; } Ruft den ursprünglichen Code auf © Arno Schmidmeier 2003 Wo? • Die Concerns überschneiden sich an bestimmten Punkten im Programm • Wir nennen diese Punkte Joinpoints • Joinpoints sind wichtige Ereignisse im Ablauf des Programms © Arno Schmidmeier 2003 Primitive pointcuts • Wir brauchen ein Prädikat, mit dem wir die relevanten Joinpoints ermittlen können. • Primitive Pointcut ist eine Art Abfrageprädikat über Join Points: – Auf das ein gegebener Join Point passt oder nicht – Das optional Werte aus dem Join Point veröffentlichen kann. – Wildcards sind möglich © Arno Schmidmeier 2003 Joinpoints • Das Aufrufen einer Methode call(<signature>) • Das Ausführen einer Methode execution(<signature>) • Das Ausführen eines Advice adviceexecution() © Arno Schmidmeier 2003 Joinpoints Zugriff auf eine Variable get(<signature>) set(<signature>) © Arno Schmidmeier 2003 Joinpoints • Das Behandeln einer Exception handler(TypePattern) • Die Initialisierung einer Klasse staticinitialization(TypePattern) • Die Initialisierung eines Objects call(ConstructorPattern) execution(ConstructorPattern) initialization(ConstructorPattern) © Arno Schmidmeier 2003 Pointcut Composition • Pointcuts können mit den logischen Operatoren ||, &&, ! verknüpft werden execution( public void Konto.*(double))|| execution(public void OtherClass.*(..)) Wann immer ( public void Konto.*(double)) oder (public void OtherClass.*(..)) ausgeführt wird. © Arno Schmidmeier 2003 Benutzerdefinierte Pointcuts • Benutzerdefinierte Pointcuts (auch bekannt als named Pointcuts) können genauso verwendet werden, wie primitive Pointcuts Name Parameter pointcut mustValidate( double Betrag): execution( public void Konto.*(double)) &&args(Betrag); © Arno Schmidmeier 2003 Wildcards in Pointcuts • * (alleine) passt auf jeden Typ inklusive primitive • * in Verbindung mit Zeichen, passt auf beliebig viele Zeichen • .. Passt auf alle Zeichen zwischen zwei . • <Typepattern>+ passt auf alle Types die Typepatten direkt matchen oder auf Typen, die von diesen abgeleitet wurden. © Arno Schmidmeier 2003 Pointcuts für die lexikalische Eingrenzung von Joinpoints • Innerhalb einer Klasse within(Type Pattern) • Innerhalb einer Methode withincode(Type Pattern) © Arno Schmidmeier 2003 Anti-Antipattern unbeschränkte Pointcuts before():call(* *.*(..){ System.out.println(..) } Problem: Unendliche Rekursion © Arno Schmidmeier 2003 Anti-Pattern: Lexikalisch Beschränkte Pointcuts aspect XY{ before():call(* *.*(..))&&!within(XY) { System.out.println(„bitte wenden“); } } aspect YZ{ before():call(* *.*(..))&&!within(YZ) { System.out.println(„bitte wenden“); } } © Arno Schmidmeier 2003 Moral: • „Reichweite“ der Pointcuts soweit wie möglich einschränken • „Reichweite“ Pointcuts nur auf die Subsysteme/ Packages beschränkten, die man selbst kontrolliert • Im Zweifellsfall Rekursion mit cflow/cflowbelow ausschliessen © Arno Schmidmeier 2003 cflow/cflowbelow • foo(){ ...bar(){ .......t1(){ ..........getX(){ ............. ..........} .......} ...} cflowbelow(p1) cflow (p1) pointcut p1():execution(* *.bar(..)) © Arno Schmidmeier 2003 100% Sichere Lösung aspect XY{ pointcut allcalls():call(* *.*(..)); before(): allcalls() &&!cflow(allcalls()) { System.out.println(“bitte wenden“); } } © Arno Schmidmeier 2003 Sichere Observer und cflow() Objekt doSomething() doSomething() Observer after(): pc() { Problem: unendliche Rekursion observerlist.notify(); } after(): pc()&&!cflow(pc()) { observerlist.notify() ; } © Arno Schmidmeier 2003 Idiom Combined Pointcut • Problem: Oft enthalten viele Pointcuts gemeinsame Fragmente pointcut p1():execution(* compo*.*()) &&(!execution(* Object.*(..))); pointcut p2():execution(* compo*.*(*,..)) &&(!execution(* Object.*(..))); © Arno Schmidmeier 2003 Idiom Combined Pointcut (2) • Lösung die gemeinsamen Elemente des Pointcuts extrahieren, und als benutzerdefinierten Pointcut wiederverwenden. pointcut p12():execution(* compo*.*()) &&notObjectCall(); pointcut p22():execution(* compo*.*(*,..)) &&notObjectCall(); pointcut notObjectCall(): !execution(* Object.*(..)); © Arno Schmidmeier 2003 Idiom: Pointcut Method • Problem: Manchmal möchte/ kann man im Pointcut nicht einfach entscheiden, ob der Joinpoint zutrifft. before():execution(* compo*.foo()) && ??called==5??{ //somecode .. } © Arno Schmidmeier 2003 Idiom: Pointcut Method • Lösung man verlagert die Entscheidung in eine Methode am Anfang des Advices before():execution(* compo*.foo()){ if (!isCalled5()) return; //rest of the advice } private boolean isCalled5(){ return called==5; } © Arno Schmidmeier 2003 If pointcut • Der if-pointcut matched alle Stellen im Code, an welchem die pointcut Bedingung wahr ist. Die Pointcut Bedingung muß statisch errechenbar sein. (darf nur statische Variablen und Funktionen nutzen) • If- pointcut konkurriert mit dem Idiom pointcut method. •Anmerkung: Ich sehe das Idiom pointcut method öfter in ApsectJ-Code, als den if pointcut © Arno Schmidmeier 2003 Typen bestimmende Pointcuts • Oft möchte man einen Pointcut auf bestimmte Typen begrenzen. • Oft möchte man Parameter und Typen aus einem Pointcut exportieren • Dafür bietet AspectJ drei Pointcuts: – target – this – args © Arno Schmidmeier 2003 Target • target(<typepattern>) • Matched alle Joinpoints, an denen das genutzte Objekt (das Objekt, auf dessen Feld zugegriffen wird oder dessen Methode aufgerufen wurde) eine Instanz des Types ist. • Matched nicht einen Aufruf einer statischen Methode oder den Zugriff auf ein statisches Feld © Arno Schmidmeier 2003 this • this(<type>) • Matched alle Joinpoints an denen, das aktuelle Object, (this) eine Instanz des Types ist. • Matched nicht im statischen Context. (statischen Funktionen) © Arno Schmidmeier 2003 args • args(<ArgumentPatternList>) • Matches jeden JoinPoint, bei dem die Argumente Instanzen im Typ mit der Argumentpatternliste übereinstimmen. args(*,TypeA,TypeX,..,String) Oder args(*,String) © Arno Schmidmeier 2003 Exportieren von Context • Ein Pointcut kann Context am Joinpoint exportieren, (zu anderen Pointcuts oder zu advices, analog zu Java-Methoden-Signaturen) • Anstelle eines Typs oder einer Liste kann bei einem this, target und einem args pointcut auch ein Identifier stehen • Der Identifier wird bei der Pointcut Deklaration oder bei der Advice Spezifikation gebunden (für anonyme pointcuts) © Arno Schmidmeier 2003 Exportieren von Context pointcut intArg(int i): args(i); pointcut publicCall(int x): call(public *.*(int)) && intArg(x); © Arno Schmidmeier 2003 Wrappen von primitiven Typen • Ausnahme: Wird ein Typ Objekt, exportiert, so matchen auch primitive Typen. Eine Instanz der Wrapperklassen wird exportiert pointcut publicCall(Object o): call(public *.*(..)) && args(o); Matched jeden Methodenaufruf, mit einem Parameter. Ist der Parameter ein int, so ist o eine Instanz von java.lang.Integer ABER: © Arno Schmidmeier 2003 Wrappen von primitiven Typen pointcut publicCall(Object o): call(public *.*(..)) && args(o); Matched jeden Methodenaufruf, mit einem Parameter. Ist der Parameter ein int, so ist o eine Instanz von java.lang.Integer ABER: pointcut publicCall(): call(public *.*(*)) && args(Object); Matched auf jeden Methodenaufruf, welcher ein Parameter vom Typ Objekt erwartet. Akzeptiert keine int © Arno Schmidmeier 2003 call und execution pointcut In 1.0.6 © Arno Schmidmeier 2003 After advice • Drei Optionen: – after(<exportierte Variablen>): – after(<exportierte Variablen>) throwing (Exception e)]: – after(<exportierte Variablen>) returning (Type ReturnValue): © Arno Schmidmeier 2003 After returning ...(2) • after returning ist am schnellsten • after throwing und after sind langsamer wegen komplexeren bytecode. • after() wird jedoch auf alle Fälle ausgeführt. © Arno Schmidmeier 2003 Nutzen des Joinpoints in Advices • Oft möchte man auf den aktuellen Joinpoint und seinem Context zugreifen. • Deshalb exisiteren in AspectJ innerhalb eines Advices drei besondere Variablen: – thisJoinPoint – thisJoinPointStaticPart – thisEnclosingJoinPointStaticPart © Arno Schmidmeier 2003 thisJoinPointStaticPart • • • • • getKind() getSourceLocation() getSignature() toLongString() toShortString() © Arno Schmidmeier 2003 thisJoinPoint • getArgs() Zugriff auf die Variablen • getThis(), getTarget() aktuelles Objekt, Target Objekt • Plus die gleichen Funktionen von thisJoinPointStaticPart © Arno Schmidmeier 2003 thisEnclosingJoinPointStaticPart • Analog zu thisJoinPointStaticPart, nur von den umgebenden Joinpoint • Manchmal aber auch identisch zu thisJoinPointStaticPart (Wenn der umgebende Joinpoint nicht berechnbar ist) © Arno Schmidmeier 2003 Das AspectCommand Idiom Beispiel: Asynchroner Methodenaufruf • Problem: Oft muß Code als Command Pattern gekapselt werden, obwohl man das eigentlich nicht braucht (aus Sicht der Business Logik) • Beispiel: Swing InvokeLater, Asynchroner Funktionsaufruf in einem eigenem Thread © Arno Schmidmeier 2003 Java Code //some Async methodcall Wird an vielen Stellen zu: new Thread(){ public void run(){ //some Async methodcall } }.start(); © Arno Schmidmeier 2003 Das gleiche mit einem Aspect • An einer Stelle: aspect AMI{ void around():call(void someobject.someasync*(..)) { Command Objekt new Thread(){ public void run(){ proceed(); } }.start(); } } © Arno Schmidmeier 2003 Exception Handling • Folgende Aufgaben – – – – – – Softening von Exceptions Exception-Konvertierung Logging von exceptions Erfassen von runtime exception Recovery Code Algorithmische Integration von Fehlverhalten © Arno Schmidmeier 2003 Aspekte • Um die neuen Konzepte modular anwenden zu können, existiert eine Art Container namens Aspekt. • Aspekte sind Klassen sehr ähnlich, Aspekte sind fast eine Obermenge von Klassen • Aspekte verhalten sich im allgemeinen zu Klassen, wie Klassen zu structs in C++ © Arno Schmidmeier 2003 Aspekte • Können Methoden, Variablen und inner classes haben, • Können advices, und pointcuts enthalten • Können interfaces implementieren • Können von Klassen abgeleitet werden © Arno Schmidmeier 2003 Einschränkungen von Aspekten • Können nur vom Laufzeitsystem instanziert werden • Inner aspects müssen als static deklariert werden. • Anonyme Aspekte sind nicht zulässig © Arno Schmidmeier 2003 Anwendung von Inner Aspects • Sun Coding Guideline: • Der Zugriff auf ein private Field muß über getter und setter Methoden durchgeführt werden auch wenn der direkte Zugriff möglich wäre. • Grund: Mögliche Änderungen können damit an nur einer Stelle durchgeführt werden © Arno Schmidmeier 2003 Beispiel Java Code • Innerhalb einer Java Klasse: private int x; private int getX(){ return x; } public int getSize(){ return getX()*getX(); } © Arno Schmidmeier 2003 Warum?!!! 1. Überflüssiger Code, der nur Performance kostet (Faktor 10) 2. Die Rückversicherung benötigt man meist eh nicht 3. Be Agile: Und wenn, dann nutzen wir einfach einen Aspekt (Die gleiche Flexibilität kann ich später immer noch hinzufügen) © Arno Schmidmeier 2003 Als Beweis: AspectJ Lösung im Fall des Falles static aspect newFuntionality{ int around():get(int x){ //do some stuff return proceed(); } } • Performance Steigerung um den Faktor 10 • Bei gleicher Flexibilität • ... © Arno Schmidmeier 2003 Aspekt Class Instanzierung • Default (oder issingleton()) – Eine Aspekt Instanz per System • percflow /percflowbelow() – Eine Aspekt Instanz per cflow() • pertarget() /perthis() –Eine Aspekt Instanz für jedes Object, das target/this am Joinpoint ist/war. © Arno Schmidmeier 2003 Namensproblem • Aspekte Umgangssprachlich angewandt beschreiben sowohl eine Aspekt-Klasse als auch eine Aspekt Instanz. • Vereinbarung: Wenn der Unterschied nicht wichtig ist oder aus dem Kontext hervorgeht, verwende ich das Wort Aspekt. Wenn der Unterschied wichtig wird und nicht aus dem Kontext hervorgeht, so verwende ich das Wort Aspekt Instanz und Aspekt Klasse © Arno Schmidmeier 2003 Zugriff auf die Aspekt Instanz [ issingleton ] aspectOf perthis() pertarget() aspectOf(Object) percflow() percflowbelow() aspectOf() © Arno Schmidmeier 2003 Pattern Whormhole • Problem: Variablen müssen über dem Callstack umständlich zum Ziel gereicht werden • Lösung: Die Werte werden in einem Aspect gespeichert. Dieser Aspekt wird als percflow/ percflowbelow deklariert. Das Ziel greift auf die Werte mit AspectOf zu. © Arno Schmidmeier 2003 Problem UserInterface: doSomething(Data,User,Password ) Layer1: validateData (Data,User,Password ) Layer2: transformData (Data,User,Password ) Brauchen wir hier nicht Layer n: processData (Data,User,Password ) DataBase: storeData (ProcessedData,User,Password ) © Arno Schmidmeier 2003 Lösung in Java • Verwendung von ThreadLocals • Dem Thread wird der User und das Passwort „angehängt“ • Danach vergessen • Wenn man es braucht, dann holt man es sich einfach vom Thread • Problem: Aufräumen der angehängten Werte © Arno Schmidmeier 2003 Lösung in AspectJ • Ein Aspekt wird als percflow/percflow below definiert. (Max. Ein Aspect pro Thread) • Der Aspekt hält die Daten • Der Zielcode greift auf die Daten per aspectOf() zu. © Arno Schmidmeier 2003 Der Code • Die Java-Methode: private void doSomethingDeep() { print(SaveUser.aspectOf().User); print(SaveUser.aspectOf().Password); } © Arno Schmidmeier 2003 Der Aspect aspect SaveUser percflow(saveuser(String,String)){ pointcut saveuser(String User,String Password): call(* userpassworddemo.doSomething(..)) &&args(..,User,Password); String User; String Password; before(String User,String Password): saveuser(User,Password) { this.User=User; this.Password=Password; } } © Arno Schmidmeier 2003 Property Pointcuts • Klassen können in AspectJ Pointcuts veröffentlichen • Motiviation: Aspekte meist an exponierten Punkten im Program • Eine Klasse kennt ihre exponierten Punkte • Sie weis aber nicht, welche Aspekte dort andocken sollen. © Arno Schmidmeier 2003 Property Pointcuts • Die Klasse veröffentlicht den Pointcut • Aspekte nutzen den Pointcut: before():someclass.WritingMethods(){ // Some stuff } © Arno Schmidmeier 2003 Coding Styleguides für advices • So knapp wie nur möglich • Auslagern von Code in Funktionen soweit wie möglich • Begründung: – Macht das advicen von advices bei Bedarf einfacher – Überschreiben von Methoden wird möglich ... © Arno Schmidmeier 2003 Aspect Inheritance Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Aspect Inheritance • Aspekte können von Klassen abgeleitet werden • Aspekte können Interfaces implementieren • Aspekte können von abstrakten Aspekten abgeleitet werden • Klassen können nicht von Aspekten abgeleitet werden © Arno Schmidmeier 2003 Aspect Konstruktor • Ein Aspekt darf höchstens einen Defaultkonstruktor besitzen – Wichtig für Ableitungen mit nicht default Konstruktoren • Der Default Konstruktor muß als public deklariert sein • Der Konstruktor darf nicht direkt aufgerufen werden © Arno Schmidmeier 2003 Abstrakt Aspect • Ein abstract Aspekt muß auch als abstract deklariert sein • Überschrieben werden können: – Methoden – Abstract pointcut • Nicht überschreibbar sind: – Concrete pointcut – advice © Arno Schmidmeier 2003 Idiom „Unknown Abstract Pointcut“ • Oft kann man einen Aspekt vollständig definieren, außer wo er angewendet werden soll. • Lösung: ein abstrakter Aspekt definiert das was und konkrete Aspekte, die den abstrakten Pointcut überschreiben, definieren das wo. © Arno Schmidmeier 2003 Pattern: Abstract Pointcut <<aspect>> AbstractAspect abstract pointcut where(); before(): hook() {…} <<aspect>> ConcreteAspect1 pointcut where(): {..} <<aspect>> ConcreteAspect2 pointcut where(): {..} © Arno Schmidmeier 2003 Warnung Ein abstrakter Pointcut kann nur von dem eigenem Aspekt oder von einem abgeleiteten Aspekt adviced oder in einem composite pointcut verwendet werden. <<aspect>> AbstractAspect abstract pointcut where(); before(): where() {…} NO <<aspect>> ConcreteAspect2 <<aspect>> ConcreteAspect2 before(): AbstractAspect.where(){. pointcut where(): {..} Das gleiche gilt für Composite mit abstrakten pointcuts. © Arno Schmidmeier 2003 Zulässig • Direkt einen <<aspect>> AbstractAspect konkreten abstract pointcut hook(); before(): hook() {…} Pointcut verwenden, der einen <<aspect>> ConcreteAspect2 abstrakten überschreibt ist pointcut hook(): {..} zulässig <<aspect>> ConcreteAspect2 before(): ConcreteAspect.hook(){.. © Arno Schmidmeier 2003 Template Advice • Manchmal möchte man Teile der Funktionalität eines Advices flexibel lassen. • Lösung: Abstrakter Aspekt, dessen konkreter Advice abstrakte Methoden aufruft • Anmerkung: Oft wird Template Advice mit abstract pointcut verknüpft. © Arno Schmidmeier 2003 Template Advice <<aspect>> AbstractAspect primitiveOperation(); pointcut pc(): ... before(): hook() { … primitiveOperation(); ... } <<aspect>> ConcreteAspect primitiveOperation() {…} © Arno Schmidmeier 2003 Wie oft? (1) public class DoubleAspect { public void foo(){} public static void main(String[] args) { new DoubleAspect().foo(); } } © Arno Schmidmeier 2003 Wie oft (2) abstract aspect DoubleAdvice{ before():call(* DoubleAspect.foo()){ System.out.println("why?"); } Ausgabe: Why? Why? } aspect xy extends DoubleAdvice{} aspect yz extends DoubleAdvice{} © Arno Schmidmeier 2003 Dynamic / Static Crosscutting • In AspectJ sprechen wir von Dynamic Crosscutting (dynamisches Crosscutting) für pointcut und advice basierte Mechanismen, die das Laufzeitverhalten eines Programms verändern. • Statisches Crosscutting hingegen verändert die Struktur des Programms © Arno Schmidmeier 2003 Q&A Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Mittagspause Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected] Themen (1) • • • • • Declare error / declare warning Softening von Exception Introduction (aka Open Classes) Verändern der Vererbungshierarchie Beispiel für Introductions: Listener Notification • Dominates versus Declare precedence (ab 1.1) © Arno Schmidmeier 2003 Themen (2) • Idiom: Aspekt Unverträglichkeit testen • Zusammenspiel vom statischem und dynamischem Croscutting Marker Interface • Observer mit Container Introduction • Q&A © Arno Schmidmeier 2003 Declare error / declare warning • Das Java Typssystem ist suboptimal • Viel zu viele Methoden müssen als public deklariert werden, und diese werden zur falschen Zeit, vom falschem Ort vom falschem Modul aufgerufen. • Beispiel: – closed subsystem – Kooperierende abstrakten Klassen © Arno Schmidmeier 2003 Declare warning/error (2) • Syntax: – declare error: <pointcut> : <„Fehlertext“> – declare warning: <pointcut> : <„Fehlertext“> declare warning: call(* java*.sql.*.*(..))&& && ! within(com.aspectsoft.sqladapter.*): "JDBC must not be used directly“ Wo immer eine Methode aus dem sql package aufgerufen wird, erzeuge einen compile Fehler, außer im Paket sqladapter © Arno Schmidmeier 2003 Softening von Exception • Ein Aspekt kann eine checked Exception an einem JoinPoint in einer org.aspectj.lang.SoftException wrappen. • SoftException ist ein Subtype von RuntimeException und muß daher nicht deklaiert werden declare soft: Type: Pointcut; • Umgeht Java's static exception Checking Mechanismus © Arno Schmidmeier 2003 Beispiel public class ExceptionSoftening { public void foo()throws SQLException{ } static aspect softer{ declare soft: SQLException: call(* *.foo()throws SQLException); } public static void main(String[] args) { new ExceptionSoftening().foo(); } } © Arno Schmidmeier 2003 Introduction (aka Open Classes) • Mit Introduction ist es einem Aspekt möglich andere Aspekte, Interfaces oder Klassen um Funktionalität zu erweitern. • Hinzugefügt werden können: – – – – Variablen, Methoden Interfaces Vaterklassen, (nur bei einer Ableitung von Object) • Bewährtes Konzept, z.B. von Python oder von der Subjektbasierten Programmierung © Arno Schmidmeier 2003 Syntax [ Modifiers ] Type OnType . Id(Formals) [ ThrowsClause ] { Body } abstract [ Modifiers ] Type OnType . Id(Formals) [ ThrowsClause ] ; [ Modifiers ] Type OnType . Id; © Arno Schmidmeier 2003 Sichtbarkeit von Privaten Introductions Code: aspect Demo{ private int A.x; } Nicht zulässig, da x nur von Demo aussichtbar ist X A Demo © Arno Schmidmeier 2003 Introduction in Interfaces • Variablen, • Methodenspezifikation, und Methoden mit Implementierung können auf Interfaces eingefügt werden © Arno Schmidmeier 2003 Introduction in Interfaces • Statische Variablen und Methoden können auf Interfaces hinzugefügt werden • Klassen die dieses Interface implementieren bekommen die neuen Funktionen. • Dies gilt selbst für statische und oder private Funktionen © Arno Schmidmeier 2003 Beispiel interface Iface {} aspect A { private void Iface.m() { System.err.println("I'm a private method"); } void worksOnI(Iface iface) { iface.m(); } } © Arno Schmidmeier 2003 Namenskonflikte • Ein Namenskonflikt zwischen einer ererbten und einer Introduceten Variable stellt einen Compile Time Error dar. • Lösung: Around Advice © Arno Schmidmeier 2003 Verändern der Vererbungshierarchie • declare parents: TypePattern extends Type; • declare parents: TypePattern implements TypeList; © Arno Schmidmeier 2003 Beispiel Listener Notification (1) KlasseA KlasseB addConsumer(..) removeConsumer(..) notify(..) foo() bar() xy() addConsumer(..) removeConsumer(..) notify(..) a() b() c() Collection Consumers Collection Consumers • Zwei Klassen, die beide die Listener notification realisieren müssen © Arno Schmidmeier 2003 Beispiel: Listener Notification (2) Problem wir haben noch immer redundanten nicht modularisierten Code, wir haben viele Aspekte mit redundanten Code... KlasseA foo() bar() xy() NotifyAspectClassA introduces introduces addConsumer(..) removeConsumer(..) notify(..) introduces Collection Consumers introduces © Arno Schmidmeier 2003 Beispiel: Listener Notification (3) Meist Introduced von irgend einem Aspekt NotifyAspect HelperNotify introduces addConsumer(..) removeConsumer(..) notify(..) KlasseA int foo() bar() xy() rod uce s Collection Consumers KlasseB a() b() c() © Arno Schmidmeier 2003 Zwei Advice am gleichem Joinpoint? • Normalerweise undeterministisches Verhalten ausser: – Von der gleichen Aspektklasse – Dann in der Reihenfolge der Definition – Abgeleiteter Aspekt dominiert „überschreibt“ den Vateraspekt • Deshalb Reihenfolge spezifizieren © Arno Schmidmeier 2003 Aspect Precedence • Bis 1.0 • Aspekt A konnte Aspekt B dominieren, ... • Beispiel: aspect X dominates B,C,D,E{ ...} aspect E dominates X { ... } • Nachteil: X muß B, C, ... kennen obwohl sie vollständig unabhängig sind, oder z.B. C muß X kennen und nicht umgekehrt. © Arno Schmidmeier 2003 Declare precedence ab 1.1 • declare precedence: TypePatternList; • Beispiel: declare precedence: *..*Security*, Logging+, *; • Maximal ein * in der Liste. • Zyklus innerhalb einer Definition ist verboten © Arno Schmidmeier 2003 Declare precedence ab 1.1 • Die declare precedence Listen werden an den Joinpoints ausgewertet, an denen zwei Advices oder introductions konkurieren • Wiedersprüchliche Declare Listen erzeugen einen Compile-Time Fehler • A -> B und B->C und C->A ist zulässig, wenn kein Joinpoint exisitiert in dem sich A, B und C überschneiden © Arno Schmidmeier 2003 Idiom: Aspekt Unverträglichkeit testen • Problem: Die Advices von Aspekt A vertragen sich nicht mit Aspekt B Dies sollte automatisch überprüft werden • Lösung: declare precedence: A,B; declare precedence: B,A; © Arno Schmidmeier 2003 Zusammenspiel vom statischem und dynamischem Croscutting • Statisches Crosscutting und dynamisches Crosscutting ergänzen sich gegenseitig ideal. • Z.B. Überschreiben versus Introduction • Dynamisches Crosscutting benötigt oft auch statische Erweiterungen der Klasse z.B. Persistenz durch Serialisierung -> implements Serialisable © Arno Schmidmeier 2003 Marker Interface <<interface>> MarkerInterface Target functional crosscutting marker <<aspect>> AspectSpecification <<aspect>> sets marker Marker Sticker © Arno Schmidmeier 2003 Observer mit Container Introduction • • • • Aufgabe für den Interessierten Zuhörer: Brauchbare Idiome: Container Introduction Marker Interface © Arno Schmidmeier 2003 Q&A Arno Schmidmeier AspectSoft +49/9151/90 50 30 [email protected]