I want my AOP!, Part 2

Learn AspectJ to better understand aspect-oriented programming

In Part 1 of this three-part series on aspect-oriented programming (AOP), I introduced AOP concepts and briefly gave AOP implementation examples. Continuing that trend in this article, I present a concrete AOP implementation for Java: AspectJ, a free implementation and language specification from Xerox PARC. Moreover, I aim to impart familiarity with the AspectJ concepts you will need for Part 3.

Read the whole "I Want My AOP" series:

In this article, I lean towards conciseness rather than completeness. For a comprehensive tutorial, I highly recommend the AspectJ Group's official AspectJ Programming Guide.

Note: You can download this article's complete source code in Resources. The sample code works with AspectJ 1.0.3 -- the latest available version at the time of publication.

AspectJ overview

AspectJ is a language specification as well as an AOP language implementation. The language specification defines various constructs and their semantics to support aspect-oriented concepts. The language implementation offers tools for compiling, debugging, and documenting code.

AspectJ's language constructs extend the Java programming language, so every valid Java program is also a valid AspectJ program. The AspectJ compiler produces class files that comply with Java byte code specification, enabling any compliant JVM to interpret the produced class files. By choosing Java as the base language, AspectJ passes on all the benefits of Java and makes it easy for Java programmers to use it.

AspectJ, as one of its strong points, features helpful tools. It provides an aspect weaver in the form of a compiler, an aspect-aware debugger and documentation generator, and a standalone aspect browser to visualize how an advice crosscuts a system's parts. Moreover, AspectJ offers good integration with popular IDEs, including Sun Microsystems' Forte, Borland's JBuilder, and Emacs, making AspectJ a useful AOP implementation for Java developers.

AspectJ language overview

To support AOP, AspectJ adds to the Java language concepts:

  • Joinpoints: Points in a program's execution. For example, joinpoints could define calls to specific methods in a class
  • Pointcuts: Program constructs to designate joinpoints and collect specific context at those points
  • Advices: Code that runs upon meeting certain conditions. For example, an advice could log a message before executing a joinpoint

Pointcut and advice together specify weaving rules. Aspect, a class-like concept, puts pointcuts and advices together to form a crosscutting unit. The pointcuts specify execution points and the weaving rule's context, whereas the advices specify actions, operations, or decisions performed at those points. You can also look at joinpoints as a set of events in response to which you execute certain advices.

I explain joinpoints, pointcuts, and advices in more detail below.

AspectJ HelloWorld

To keep with the tradition of introducing a new programming language with a HelloWorld program, let's write one in AspectJ.

First, here's a simple class containing methods to print a message:

// HelloWorld.java
public class HelloWorld {
    public static void say(String message) {
        System.out.println(message);
    }
    
    public static void sayToPerson(String message, String name) {
        System.out.println(name + ", " + message);
    }
}

Let's now write an implementation for adding greeting and gratitude manners. Before the program prints a message, it should print "Good day!"; and should follow the message with "Thank you!":

// MannersAspect.java
public aspect MannersAspect {
    pointcut callSayMessage() : call(public static void HelloWorld.say*(..));
    before() : callSayMessage() {
        System.out.println("Good day!");
    }
    after() : callSayMessage() {
        System.out.println("Thank you!");
    }
}

The MannersAspect.java file, in a process similar to class declaration in Java, declares a MannersAspect aspect. The aspect defines a callSayMessage() pointcut that captures calls to all public static methods with names that start with say. In our example, it would capture say() and sayToPerson() in a HelloWorld class taking any arguments. Then you define two advices for before and after reaching the callSayMessage() pointcut: printing "Good day!" and "Thank you!" When you compile the program with the AspectJ compiler, then run it, you'll see "Good day!" printed before each message, and "Thank you!" after each message. You now have excellent manners!

AspectJ language concepts and constructs

Now that you have a feel for the AspectJ language, let's examine its core constructs in greater detail. This section explains AspectJ's available joinpoints and various pointcut syntax. I also take a closer look at advice constructs.

Joinpoints

Joinpoints, a central concept in AspectJ, are well-defined points in a program's execution. Candidate joinpoints include calls to a method, a conditional check, a loop's beginning, or an assignment. Joinpoints also have a context associated with them. For example, a method-call joinpoint could have the target object and its argument as part of the context.

Although any identifiable point in a program's execution is a joinpoint, AspectJ limits the available joinpoints to those usable in a systematic manner. AspectJ makes the following pointcuts available:

  • Method call and execution
  • Constructor call and execution
  • Read/write access to a field
  • Exception handler execution
  • Object and class initialization execution

AspectJ does not expose finer execution structures such as if checks or for loops.

Pointcuts

Pointcuts, program constructs to designate joinpoints, let you specify a joinpoint collection. Pointcuts also let you expose context at the joinpoint to an advice implementation.

In the HelloWorld example above, I used the following pointcut:

pointcut callSayMessage() : call(public static void HelloWorld.say*(..));

In the code above, pointcut declares that what follows is a declaration of a named pointcut. Next, callSayMessage(), the pointcut's name, resembles a method declaration. The trailing empty () suggests that the pointcut collects no context.

Moving on, call(public static void HelloWorld.say*(..)) captures needed joinpoints. call indicates the pointcut captures a call to, as opposed to execution of, designated methods. The public static void HelloWorld.say*(..) is the signature for methods to be captured. public static indicates that the methods must have public access and be declared a static method. void says that methods captured must have a void return type. HelloWorld.say* specifies the to-be-captured method's class and name. Here, we are specifying HelloWorld as a class.

say* uses the * wildcard to indicate the capture of methods with names starting with "say." Finally, (..) specifies arguments to the captured methods. In this case, you specify the .. wildcard to capture methods regardless of type and number of arguments they take.

Now that you know how to specify pointcuts to capture joinpoints, let's look briefly at other pointcuts types.

Call to methods and constructors pointcuts

First, call to methods and constructors pointcuts capture execution points after they evaluate a method's arguments, but before they call the method itself. They take the form of call(MethodOrConstructorSignature). Table 1 shows examples of such pointcuts.

Table 1. Call to methods and constructors pointcuts
Pointcut
Description
call(public void MyClass.myMethod(String))Call to myMethod() in MyClass taking a String argument, returning void, and with public access
call(void MyClass.myMethod(..))Call to myMethod() in MyClass taking any arguments, with void return type, and any access modifiers
call(* MyClass.myMethod(..))Call to myMethod() in MyClass taking any arguments returning any type
call(* MyClass.myMethod*(..))Call to any method with name starting in "myMethod" in MyClass
call(* MyClass.myMethod*(String,..))Call to any method with name starting in "myMethod" in MyClass and the first argument is of String type
call(* *.myMethod(..))Call to myMethod() in any class in default package
call(MyClass.new())Call to any MyClass' constructor taking no arguments
call(MyClass.new(..))Call to any MyClass' constructor with any arguments
call(MyClass+.new(..))Call to any MyClass or its subclass's constructor. (Subclass indicated by use of '+' wildcard)
call(public * com.mycompany..*.*(..))All public methods in all classes in any package with com.mycompany the root package

Execution of methods and constructors pointcuts

Next, execution of methods and constructors pointcuts capture the method's execution. In contrast to call pointcuts, execution pointcuts represent the method or constructor body itself. They take the form of execution(MethodOrConstructorSignature).

Table 2. Execution of methods and constructors pointcuts
Pointcut
Description
execution(public void MyClass.myMethod(String))Execution of myMethod() in MyClass taking a String argument, returning void, and with public access
execution(void MyClass.myMethod(..))Execution of myMethod() in MyClass taking any arguments, with void return type, and any access modifiers
execution(* MyClass.myMethod(..))Execution of myMethod() in MyClass taking any arguments returning any type
execution(* MyClass.myMethod*(..))Execution of any method with name starting in "myMethod" in MyClass
execution(* MyClass.myMethod*(String,..))Execution of any method with name starting in "myMethod" in MyClass and the first argument is of String type
execution(* *.myMethod(..))Execution of myMethod() in any class in default package
execution(MyClass.new())Execution of any MyClass' constructor taking no arguments
execution(MyClass.new(..))Execution of any MyClass' constructor with any arguments
execution(MyClass+.new(..))Execution of any MyClass or its subclass's constructor. (Subclass indicated by use of '+' wildcard)
execution(public * com.mycompany..*.*(..))All public methods in all classes in any package with com.mycompany the root package

Field-access pointcuts

Third, field-access pointcuts capture read and write access to a class's field. For example, you can capture all access to the out field inside the System class (as in System.out). You can capture either read or write access. For example, you can capture writing into field x of MyClass, as in MyClass.x = 5. The read-access pointcut takes the form of get(FieldSignature), whereas the write-access pointcut takes the form of set(FieldSignature). FieldSignature can use wildcards in the same manner as MethodOrConstructor in call and execution pointcuts.

Table 3. Field-access pointcuts
Pointcut
Description
get(PrintStream System.out)Execution of read-access to field out of type PrintStream in System class
set(int MyClass.x)Execution of write-access to field x of type int in MyClass

Exception-handler pointcuts

Fourth, exception-handler pointcuts capture the execution of exception handlers of specified types. They take the form of handler(ExceptionTypePattern).

Table 4. Exception-handler pointcuts
Pointcut
Description
handler(RemoteException)Execution of catch-block handling RemoteException type
handler(IOException+)Execution of catch-block handling IOException or its subclasses
handler(CreditCard*)Execution of catch-block handling exception types with names that start with CreditCard

Class-initialization pointcuts

Fifth, class-initialization pointcuts capture the execution of static-class initialization, code specified in static blocks inside class definitions, of specified types. They take the form of staticinitialization(TypePattern).

Table 5. Class-initialization pointcuts
Pointcut
Description
staticinitialization(MyClass)Execution of static block of MyClass
Staticinitialization(MyClass+)Execution of static block of MyClass or its subclasses

Lexical-structure-based pointcuts

Next, lexical-structure-based pointcuts capture all joinpoints inside a class or method's lexical structure. The pointcut capturing the code lexically inside a class, including an inner class, takes the form of within(TypePattern). The pointcut capturing the code lexically inside a method or constructor, including any local classes, takes the form of withincode(MethodOrConstructorSignature).

Table 6. Lexical-structure-based pointcuts
Pointcut
Description
within(MyClass)Any pointcut inside MyClass's lexical scope
within(MyClass*)Any pointcut inside lexical scope of classes with a name that starts with "MyClass"
withincode(* MyClass.myMethod(..))Any pointcut inside lexical scope of any myMethod() of MyClass

Control-flow-based pointcuts

Seventh, control-flow-based pointcuts capture pointcuts based on other pointcuts' control flow (the flow of program instructions). For example, if in an execution, method a() calls method b(), then b() is said to be in a()'s control flow. With control-flow-based pointcuts, you can, for example, capture all methods, field access, and exception handlers caused by invoking a method. The pointcut that captures the pointcuts in control flow of some other pointcut, including the specified pointcut itself, takes the form of cflow(pointcut), whereas the one that excludes the specified pointcut itself takes the form of cflowbelow(pointcut).

Related:
1 2 3 Page 1
Page 1 of 3