AspectJ Semantics

This section presents a brief introduction to the features of AspectJ used later in this chapter. These features are at the core of the language, but this is by no means a complete overview of AspectJ.

The semantics are presented using a simple figure editor system. A Figure consists of a number of FigureElements, which can be either Points or Lines. The Figure class provides factory services. There is also a Display. Most example programs later in this chapter are based on this system as well.

UML for the FigureEditor example

The motivation for AspectJ (and likewise for aspect-oriented programming) is the realization that there are issues or concerns that are not well captured by traditional programming methodologies. Consider the problem of enforcing a security policy in some application. By its nature, security cuts across many of the natural units of modularity of the application. Moreover, the security policy must be uniformly applied to any additions as the application evolves. And the security policy that is being applied might itself evolve. Capturing concerns like a security policy in a disciplined way is difficult and error-prone in a traditional programming language.

Concerns like security cut across the natural units of modularity. For object-oriented programming languages, the natural unit of modularity is the class. But in object-oriented programming languages, crosscutting concerns are not easily turned into classes precisely because they cut across classes, and so these aren't reusable, they can't be refined or inherited, they are spread through out the program in an undisciplined way, in short, they are difficult to work with.

Aspect-oriented programming is a way of modularizing crosscutting concerns much like object-oriented programming is a way of modularizing common concerns. AspectJ is an implementation of aspect-oriented programming for Java.

AspectJ adds to Java just one new concept, a join point, and a few new constructs: pointcuts, advice, introduction and aspects. Pointcuts and advice dynamically affect program flow, and introduction statically affects a program's class heirarchy.

A join point is a well-defined point in the program flow. Pointcuts select certain join points and values at those points. Advice defines code that is executed when a pointcut is reached. These are, then, the dynamic parts of AspectJ.

AspectJ also has a way of affecting a program statically. Introduction is how AspectJ modifies a program's static structure, namely, the members of its classes and the relationship between classes.

The last new construct in AspectJ is the aspect. Aspects, are AspectJ's unit of modularity for crosscutting concerns They are defined in terms of pointcuts, advice and introduction.

In the sections immediately following, we are first going to look at join points and how they compose into pointcuts. Then we will look at advice, the code which is run when a pointcut is reached. We will see how to combine pointcuts and advice into aspects, AspectJ's reusable, inheritable unit of modularity. Lastly, we will look at how to modify a program's class structure with introduction.

The Dynamic Join Point Model

A critical element in the design of any aspect-oriented language is the join point model. The join point model provides the common frame of reference that makes it possible to define the dynamic structure of crosscutting concerns.

This chapter describes AspectJ's dynamic join points, in which join points are certain well-defined points in the execution of the program. Later we will discuss introduction, AspectJ's form for modifying a program statically.

AspectJ provides for many kinds of join points, but this chapter discusses only one of them: method call join points. A method call join point encompasses the actions of an object receiving a method call. It includes all the actions that comprise a method call, starting after all arguments are evaluated up to and including normal or abrupt return.

Each method call itself is one join point. The dynamic context of a method call may include many other join points: all the join points that occur when executing the called method and any methods that it calls.

Pointcut Designators

In AspectJ, pointcut designators (or simply pointcuts) identify certain join points in the program flow. For example, the pointcut

call(void Point.setX(int))

identifies any call to the method setX defined on Point objects. Pointcuts can be composed using a filter composition semantics, so for example:

call(void Point.setX(int)) ||
call(void Point.setY(int))

identifies any call to either the setX or setY methods defined by Point.

Programmers can define their own pointcuts, and pointcuts can identify join points from many different classes — in other words, they can crosscut classes. So, for example, the following declares a new, named pointcut:

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));

The effect of this declaration is that move is now a pointcut that identifies any call to methods that move figure elements.

Property-Based Primitive Pointcuts

The previous pointcuts are all based on explicit enumeration of a set of method signatures. We call this name-based crosscutting. AspectJ also provides mechanisms that enable specifying a pointcut in terms of properties of methods other than their exact name. We call this property-based crosscutting. The simplest of these involve using wildcards in certain fields of the method signature. For example:

call(void Figure.make*(..))

identifies calls to any method defined on Figure, for which the name begins with "make", specifically the factory methods makePoint and makeLine; and

call(public * Figure.* (..))

identifies calls to any public method defined on Figure.

One very powerful primitive pointcut, cflow, identifies join points based on whether they occur in the dynamic context of another pointcut. So

cflow(move())

identifies all join points that occur between receiving method calls for the methods in move and returning from those calls (either normally or by throwing an exception.)

Advice

Pointcuts are used in the definition of advice. AspectJ has several different kinds of advice that define additional code that should run at join points. Before advice runs when a join point is reached and before the computation proceeds, i.e. it runs when computation reaches the method call and before the actual method starts running. After advice runs after the computation 'under the join point' finishes, i.e. after the method body has run, and just before control is returned to the caller. Around advice runs when the join point is reached, and has explicit control over whether the computation under the join point is allowed to run at all. (Around advice and some variants of after advice are not discussed in this chapter.)

after(): move() {
    System.out.println("A figure element moved.");
}

Exposing Context in Pointcuts

Pointcuts can also expose part of the execution context at their join points. Values exposed by a pointcut can be used in the body of advice declarations. In the following code, the pointcut exposes three values from calls to setXY: the FigureElement receiving the call, the new value for x and the new value for y. The advice then prints the figure element that was moved and its new x and y coordinates after each setXY method call.

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): setXY(fe, x, y) {
   System.out.println(fe + " moved to (" + x + ", " + y + ").");
}

Introduction

Introduction is AspectJ's form for modifying classes and their hierarchy. Introduction adds new members to classes and alters the inheritance relationship between classes. Unlike advice that operates primarily dynamically, introduction operates statically, at compilation time. Introduction changes the declaration of classes, and it is these changed classes that are inherited, extended or instantiated by the rest of the program.

Consider the problem of adding a new capability to some existing classes that are already part of a class heirarchy, i.e. they already extend a class. In Java, one creates an interface that captures this new capability, and then adds to each affected class a method that implements this interface.

AspectJ can do better. The new capability is a crosscutting concern because it affects multiple classes. Using AspectJ's introduction form, we can introduce into existing classes the methods or fields that are necessary to implement the new capability.

Suppose we want to have Screen objects observe changes to Point objects, where Point is an existing class. We can implement this by introducing into the class Point an instance field, observers, that keeps track of the Screen objects that are observing Points. Observers are added or removed with the static methods addObserver and removeObserver. The pointcut changes defines what we want to observe, and the after advice defines what we want to do when we observe a change. Note that neither Screen's nor Point's code has to be modified, and that all the changes needed to support this new capability are local to this aspect.

aspect PointObserving {

   private Vector Point.observers = new Vector();

   public static void addObserver(Point p, Screen s) {
      p.observers.add(s);
   }

   public static void removeObserver(Point p, Screen s) {
      p.observers.remove(s);
   }

   pointcut changes(Point p): target(p) && call(void Point.set*(int));

   after(Point p): changes(p) {
      Iterator iter = p.observers.iterator();
      while ( iter.hasNext() ) {
         updateObserver(p, (Screen)iter.next());
      }
   }

   static void updateObserver(Point p, Screen s) {
      s.display(p);
   }
}

Aspect Declarations

An aspect is a modular unit of crosscutting implementation. It is defined very much like a class, and can have methods, fields, and initializers. The crosscutting implementation is provided in terms of pointcuts, advice and introductions. Only aspects may include advice, so while AspectJ may define crosscutting effects, the declaration of those effects is localized.

The next three sections present the use of aspects in increasingly sophisticated ways. Development aspects are easily removed from production builds. Production aspects are intended to be used in both development and in production, but tend to affect only a few classes. Finally, reusable aspects require the most experience to get right.