Pointcut - Arno Schmidmeier, AspectSoft

Werbung
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]
Herunterladen