Programmation par aspects
XML parsing dans Tomcat
Construction du journal dans Tomcat
Le rouge montre les lignes de code qui traitent du logging Très mal modularisé
Le code traite de plusieurs aspects
class Fraction { int numerator; int denominator; ... public Fraction multiply(Fraction that) { traceEnter("multiply", new Object[] {that}); Fraction result = new Fraction( this.numerator * that.numerator, this.denominator * that.denominator); result = result.reduceToLowestTerms(); traceExit("multiply", result); return result; } ...}
Préoccupations
Préoccupations métier :
Effectuer des paiements : débiter un montant d'un compte défini
Préoccupations techniques :
Traces
Intégrité de la transaction
Identification / authentification
Sécurité (confidentialité)
Performance
etc.
Dispersion / entrelacement des préoccupations
Chaque objet métier gère ses propres contraintes techniques (à son niveau) :
les préoccupations sont dispersées / entrelacées
Les applications sont plus difficiles à :
Concevoir / coder
Comprendre
Maintenir
Faire évoluer...
Symptômes
L’embrouillement du code– Les objets métiers interagissent simultanément avec de
nombreuses préoccupations techniques– Présence simultanée d’éléments de chaque préoccupation
dans le code
L’étalement du code– Les préoccupations entrelacées, s’étalent sur de nombreux
modules. – Leur implémentation s’étale elle aussi sur de nombreux
objets
Conséquences
Redundant code– Same fragment of code in many places
Difficult to reason about– Non-explicit structure– The big picture of the tangling isn’t clear
Difficult to change– Have to find all the code involved...– ...and be sure to change it consistently– ...and be sure not to break it by accident
Inefficient when crosscuting code is not needed
Solutions actuelles
mix-in classes– Une classe qui fournit des fonctionnalités à être héritées par une autre
classe, mais qui n’a pas de raisons d’être en soi. – Inheriting from a mixin is not a form of specialization but is rather a
means to collect functionality.
design patterns– Exemple Visitor et Template Method permettent de différer
l’implémentation
domain-specific solutions – frameworks and application servers,
let developers address some crosscutting concerns in a modularized way. The Enterprise JavaBeans (EJB) architecture, for example, addresses
crosscutting concerns such as security, administration, performance, and container-managed persistence.
Introduction
What is a crosscutting concern?
– Behavior that cuts across the typical divisions of responsibility, such as logging or debugging
– A problem which a program tries to solve.
– Aspects of a program that do not relate to the core concerns directly, but which proper program execution nevertheless requires.
LanguageDynamic VS Static crosscutting
Dynamic crosscutting– define additional behavior to run at certain well-defined
points in the execution of the program
Static crosscutting– modify the static structure of a program
adding new methods, implementing new interfaces, modifying the class hierarchy, etc.
Programmation par aspects
Implémenter des préoccupations de façon indépendante
Combiner ces implémentation pour former le système final
L’AOP est l’évolution logique de la programmation orientée objet
Etapes de développement de l’AOP
Identifier le préoccupations métier (classes) et techniques (aspects)
Implémenter chaque préoccupation séparément
Tisser les différentes préoccupations
Deux approches de l’AOP
AspectJ JAC
Extension du langage Java Framework AOP, serveur d’applications
Nouvelle grammaire pour exprimer les aspects
Aspects écrits en pur Java
Travaille sur le code source. Chaque modification nécessite une recompilation
Travaille sur le bytecode, permet de modifier, d’ajouter ou de retirer des aspects
dynamiquement
Ne gère pas la distribution Distribue automatiquement les aspects sur des serveurs distants
Permet uniquement le développement d’aspects
Permet le développement et la configuration des aspects
Support IDE pour JBuilder, Forte, et Emacs IDE UML gérant les aspects
Pas d’aspects pré-développés configurables Ensemble d’aspects pré-développés configurables
Version courante 1.0.5 Version courante 0.9.1
Open Source Mozilla Public License LGPL
Aspect J
The Figure Element example
Example I
A pointcut named move that chooses various method calls:– pointcut move():
call(void FigureElement.setXY(int,int)) || call(void Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point));
Advice (code) that runs before the move pointcut:– before(): move() {
System.out.println("About to move");}
Advice that runs after the move pointcut:– after(): move() {
System.out.println("Just successfully moved");}
LanguageJoin Points
Well-defined points in the execution of a program:– Method call, Method execution– Constructor call, Constructor execution– Static initializer execution – Object pre-initialization, Object initialization– Field reference, Field set– Handler execution– Advice execution
LanguageJoin Points
public class Point { private int x; private int y;
public Point(int x, int y) { this.x = x; this.y = y;} public void incrXY(int dx, int dy){ x=+dx; y=+dy;} …}
public class Test{ public static void main(String[] args) { Point pt1 = new Point(0,0);
} } pt1.incrXY(3,6);
Method-execution
“Test.main(..)”
Staticinitialization
“Point._clinit_”
Constructor-call
“Point(..)”
Preinitialization
“Point(..)” Initialization
“Point(..)”
Constructor-execution
“Point(..)”
Field-set
“Point.x”Field-set
“Point.y”
Method-call
“Point.incrXY(..)”
Method-execution
“Point.incrXY(..)”Field-set
“Point.y”
Field-set
“Point.x”
Join points
A join point is a well-defined point in the program flow– We want to execute some code (“advice”) each time a join point is
reached– We do not want to clutter up the code with explicit indicators
saying “This is a join point”– AspectJ provides a syntax for indicating these join points “from
outside” the actual code A join point is a point in the program flow “where something
happens”– Examples:
When a method is called When an exception is thrown When a variable is accessed
Example pointcut designators I
When a particular method body executes:– execution(void Point.setX(int))
When a method is called:– call(void Point.setX(int))
When an exception handler executes:– handler(ArrayOutOfBoundsException)
When the object currently executing (i.e. this) is of type SomeType: – this(SomeType)
Example pointcut designators II
When the target object is of type SomeType – target(SomeType)
When the executing code belongs to class MyClass – within(MyClass)
When the join point is in the control flow of a call to a Test's no-argument main method– cflow(call(void Test.main()))
Pointcut designator wildcards
It is possible to use wildcards to declare pointcuts:– execution(* *(..))
Chooses the execution of any method regardless of return or parameter types
– call(* set(..)) Chooses the call to any method named set regardless
of return or parameter type In case of overloading there may be more than one such
set method; this pointcut picks out calls to all of them
Pointcut designators based on types
You can select elements based on types. For example,– execution(int *())
Chooses the execution of any method with no parameters that returns an int
– call(* setY(long)) Chooses the call to any setY method that takes a long as an argument,
regardless of return type or declaring type
– call(* Point.setY(int)) Chooses the call to any of Point’s setY methods that take an int as an
argument, regardless of return type
– call(*.new(int, int)) Chooses the call to any classes’ constructor, so long as it takes exactly
two ints as arguments
Pointcut designator composition
Pointcuts compose through the operations or (“||”), and (“&&”) and not (“!”)
Examples:– target(Point) && call(int *())
Chooses any call to an int method with no arguments on an instance of Point, regardless of its name
– call(* *(..)) && (within(Line) || within(Point)) Chooses any call to any method where the call is made from the code in Point’s or
Line’s type declaration
– within(*) && execution(*.new(int)) Chooses the execution of any constructor taking exactly one int argument, regardless
of where the call is made from
– !this(Point) && call(int *(..)) Chooses any method call to an int method when the executing object is any type
except Point
Pointcut designators based on modifiers
call(public * *(..)) – Chooses any call to a public method
execution(!static * *(..)) – Chooses any execution of a non-static method
execution(public !static * *(..)) – Chooses any execution of a public, non-static method
Pointcut designators can be based on interfaces as well as on classes
Kinds of advice
AspectJ has several kinds of advice; here are some of them:– Before advice runs as a join point is reached, before the program
proceeds with the join point
– After advice on a particular join point runs after the program proceeds with that join point
after returning advice is executed after a method returns normally after throwing advice is executed after a method returns by throwing
an exception after advice is executed after a method returns, regardless of whether
it returns normally or by throwing an exception
– Around advice on a join point runs as the join point is reached, and has explicit control over whether the program proceeds with the join point
Example II, with parameters
You can access the context of the join point:
pointcut setXY(FigureElement fe, int x, int y): call(void FigureElement.setXY(int, int)) && target(fe) && args(x, y);
after(FigureElement fe, int x, int y) returning: setXY(fe, x, y) { System.out.println(fe + " moved to (" + x + ", " + y + ").");}
Introduction
An introduction is a member of an aspect, but it defines or modifies a member of another type (class). With introduction we can– add methods to an existing class– add fields to an existing class– extend an existing class with another– implement an interface in an existing class– convert checked exceptions into unchecked
exceptions
Example introduction
aspect CloneablePoint {
declare parents: Point implements Cloneable;
declare soft: CloneNotSupportedException: execution(Object clone());
Object Point.clone() { return super.clone(); }}
Approximate syntax
An aspect is: aspect nameOfAspect { body }– An aspect contains introductions, pointcuts, and advice
A pointcut designator is: when(signature)– The signature includes the return type– The “when” is call, handler, execution, etc.
A named pointcut designator is: name(parameters): pointcutDesignator
Advice is: adviceType(parameters): pointcutDesignator { body }
Introductions are basically like normal Java code
Example aspect I
aspect PointWatching {
private Vector Point.watchers = new Vector();
public static void addWatcher(Point p, Screen s) { p.Watchers.add(s); }
public static void removeWatcher(Point p, Screen s) { p.Watchers.remove(s); }
static void updateWatcher(Point p, Screen s) { s.display(p); }// continued on next slide
Example aspect II
// continued from previous slide
pointcut changes(Point p): target(p) && call(void Point.set*(int));
after(Point p): changes(p) { Iterator iter = p.Watchers.iterator(); while ( iter.hasNext() ) { updateWatcher(p, (Screen)iter.next()); } }}
Références
The AspectJTM Programming Guide– http://www.eclipse.org/aspectj/doc/released/progguide/index.html
I want my AOP!– http://www.javaworld.com/javaworld/jw-01-2002/jw-0118-aspect.html
Aspect-Oriented Programming with AspectJ™– http://dolphin.c.u-tokyo.ac.jp/~kazu0/aspectj/doc/tutorial/aspectj-tutorial.pdf
www.cs.cmu.edu/~aldrich/courses/819/slides/aspectj.ppt
http://www.eclipse.org/ajdt/demos/HelloWorldAspectJ.html
LanguagePointcuts
A set of join point, plus, optionally, some of the values in the execution context of those join points.
Can be composed using boolean operators || , &&
Matched at runtime
LanguageAdvice
Method-like mechanism used to declare that certain code should execute at each of the join points in the pointcut.
Advice:– before– around– after
after after returning after throwing
LanguageAdvice
Method-like mechanism used to declare that certain code should execute at each of the join points in the pointcut.
Advice:– before– around– after
after after returning after throwing
LanguagePointcut parameters and thisJoinPoint
thisJoinPoint
Simple reflective access to information about the current join point. Notice that this is very useful for debugging and tracing purposes.
before():call(void Point.setX(int)){
if(((Integer)thisJoinPoint.getArgs()[0]).intValue()>10)
elseSystem.out.println("x value of ”+thisJoinPoint.getTarget() +" will be set to”
+thisJoinPoint.getArgs()[0]);}
System.out.println("The value is too big !");
LanguageWildcards and cflow
Wildcards– call( * Point. *(..))– call( public * com.xerox.scanner.*.*(..))– call( * Point.get*())
LanguageWildcards and cflow
Control-flow– pointcut moveContext(): cflow(move())
picks out each join point that occurs in the dynamic context of the join points picked out by move().
LanguageWildcards and cflow
Control-flow belowpointcut moves(FigureElement fe):
…);
pointcut topLevelMoves(FigureElement fe):target(fe)&&moves(FigureElement)&&!cflowbelow(moves(FigureElement));
before(FigureElement fe):target(fe)&&topLevelMoves(FigureElement)}
{System.out.println("top level moves");
target(fe)&&
LanguageInheritance and Overriding of Advice and pointcuts
Abstract aspect & Abstract pointcutsabstract aspect SimpleTracing{ abstract pointcut tracePoints(); before(): tracePoints() { printMessage(“Entering”,thisJoinPoint); } after(): tracePoints(){
printMessage(“Exiting”,thisJoinPoint); }
void printMessage(Stringwhen,JoinPoint jpt){ code to print an informative message using information
from the joinpoint }
}
LanguageInheritance and Overriding of Advice and pointcuts
Concrete implementation of the aspect:
aspect IncrXYTracing extends SimpleTracing{
pointcut tracePoints():
call(void FigureElement.incrXY(int, int));
}
Conséquences
Poor traceability: Simultaneously implementing several concerns obscures the correspondence between a concern and its implementation, resulting in a poor mapping between the two.
Lower productivity: Simultaneous implementation of multiple concerns shifts the developer's focus from the main concern to the peripheral concerns, leading to lower productivity.
Less code reuse: Since, under these circumstances, a module implements multiple concerns, other systems requiring similar functionality may not be able to readily use the module, further lowering productivity.
Poor code quality: Code tangling produces code with hidden problems. Moreover, by targeting too many concerns at once, one or more of those concerns will not receive enough attention.
More difficult evolution: A limited view and constrained resources often produce a design that addresses only current concerns. Addressing future requirements often requires reworking the implementation. Since the implementation is not modularized, that means touching many modules. Modifying each subsystem for such changes can lead to inconsistencies. It also requires considerable testing effort to ensure that such implementation changes have not caused bugs.