I want my AOP!, Part 2

Learn AspectJ to better understand aspect-oriented programming

1 2 3 Page 2
Page 2 of 3
Table 7. Control-flow-based pointcuts
Pointcut
Description
cflow(call(* MyClass.myMethod(..))All the joinpoints in control flow of call to any myMethod() in MyClass including call to the specified method itself
cflowbelow(call(* MyClass.myMethod(..))All the joinpoints in control flow of call to any myMethod() in MyClass excluding call to the specified method itself

Self-, target-, and arguments-type pointcuts

Eighth, self-, target-, and arguments-type pointcuts capture joinpoints based on the self-object, target-object, and argument types. These are the only constructs that can capture context at the joinpoints. The pointcut that captures the pointcuts based on the self object takes the form of this(TypePattern or ObjectIdentifier), whereas the one based on target takes the form of target(TypePattern or ObjectIdentifier). Argument-based pointcuts take the form of args(TypePattern or ObjectIdentifier, ..).

Table 8. Self-, target-, and arguments-type pointcuts
Pointcut
Description
this(JComponent+)All the joinpoints where this is instanceof JComponent
target(MyClass)All the joinpoints where the object on which the method is called is of type MyClass
args(String,..,int)All the joinpoints where the first argument is of String type and the last argument is of int type
args(RemoteException)All the joinpoints where the type of argument or exception handler type is RemoteException

Conditional-test pointcuts

The ninth and last pointcut we'll look at, conditional test, captures joinpoints based on some conditional check at the joinpoint. It takes the form of if(BooleanExpression).

Table 9. Conditional-test pointcuts
Pointcut
Description
if(EventQueue.isDispatchThread())All the joinpoints where EventQueue.isDispatchThread() evaluates to true

Named and anonymous pointcuts

Named pointcuts explicitly specify their names. You can reuse such pointcuts for defining other pointcuts, defining part of an advice, overriding a pointcut, and so on.

Anonymous pointcuts, like anonymous classes, become defined at the point of their usage, specifically in the advice specification or as a part of some other pointcut's definition. As with anonymous classes, anonymous pointcuts cannot be reused. In the examples above, I used an anonymous pointcut with cflow().

Use ||, &&, and ! operators with pointcuts

You can use || and && operators to combine named and anonymous pointcuts. For example, to designate a pointcut for a call to method m1() or m2() in MyClass, you could use call(* MyClass.m1()) || call(* MyClass.m2()). To designate a pointcut for a call to method m1() in MyClass that is in control flow of a call to m2 in MyClass, you could use call(* MyClass.m1()) && cflow(call(* MyClass.m2()).

You can use broad pointcuts such as this(), within(), and cflow() with other pointcuts using && to capture a smaller joinpoint subset.

Use ! to specify joinpoints not captured by the specified pointcut. For example, to designate a call to all methods, except m1() in MyClass, you could use a !call(* MyClass.m1()) pointcut.

Exposing context

Advice implementations often require access to data at the joinpoint. For example, to log certain operations, an advice might need information, called context, about a method and arguments to it. Pointcuts, therefore, allow exposure of the context at the execution point to pass to an advice implementation. AspectJ offers target(), this(), and args() pointcuts to collect the context. For example, the following pointcut collects all arguments to method executions associated with it:

    pointcut publicOperationCardAmountArgs(CreditCard card, Money amount):
        execution(public * CreditCardProcessor.*(..)) && args(card, amount);

Reflection

AspectJ supports a limited form of reflection. With AspectJ's reflection, you can examine information at any pointcut's execution point. Each advice body has access to a special object, thisJoinPoint, which contains the information about the joinpoint. Such reflective access proves particularly important in logging and debugging aspects.

Advices

Advices specify the executable code when reaching certain pointcuts. AspectJ provides three ways to associate an advice with a joinpoint: before, after, and around a joinpoint's execution. A before advice runs just before the joinpoint, whereas an after advice runs just after. With an after advice, you can specify after normal return, after throwing an exception, or after returning either way from a joinpoint. An around advice surrounds a joinpoint and has control if the joinpoint's execution should proceed. It can also decide to proceed with a different argument set.

Let's look at a few simple advice examples.

The following advice example prints thisJoinPoint and the current system time before and after calling any public method in MyClass:

before() : call(public * MyClass.*(..)) {
        System.out.println("Before: " + thisJoinPoint + " " + 
System.currentTimeMillis());
}
after() : call(public * MyClass.*(..)) {
        System.out.println("After: " + thisJoinPoint + " " + 
System.currentTimeMillis());
}

The next advice example captures all calls to Connection.close(), and, if you've enabled connection pooling, puts the connection into a resource pool instead; otherwise it uses proceed() to proceed with the captured operation. This advice also uses context collected by target():

void around(Connection conn) : call(Connection.close()) && target(conn) {
    if (enablePooling) {
        connectionPool.put(conn);
    } else {
        proceed();
    }
}

Aspects

Aspects act as AspectJ's unit of modularization, just as classes do in Java. An aspect puts together pointcuts and advices. Aspects resemble classes: an aspect can contain methods and fields, extend other classes or aspects, and implement interfaces. However, aspects differ from classes in that you cannot create an object for an aspect using new.

AspectJ allows classes to declare pointcuts. You must declare static pointcuts declared inside a class. However, AspectJ does not allow classes to contain advices; only aspects can contain advices.

An aspect can mark itself and any pointcut as abstract. Abstract pointcuts act similarly to a class's abstract methods: they let you defer the details to the derived aspects. A concrete aspect extending an abstract aspect can then provide concrete definitions of abstract pointcuts.

AspectJ examples

With your new AspectJ knowledge, it's time to look at a few examples implemented in AspectJ. (For another AOP implementation, review Part 1's credit card example. In Part 3, I will present more complex AOP examples.)

Example 1: Detect a Swing UI (user interface) update from a nonevent dispatch thread

The following aspect detects any call to a Swing model that updates the UI. (For more information about when to use such detection, see Resources.) The test code that exercises this aspect is in Test.java:

// SwingThreadSafetyCheckAspect.java
import java.awt.*;
/**
 * Aspect that warns if Swing components are updated from a
 * nonevent dispatch thread.
 *
 * Because AspectJ currently does not allow byte-code weaving, the 
 * implementation is short. It is required to manually
 * encode each method that updates model. Besides, it does not take care of
 * custom models.
 *
 * The implementation is for illustration only. It takes care of only
 * part of DefaultTableModel and DefaultListModel. 
 */
public aspect SwingThreadSafetyCheckAspect {
    private static final String WARNING_MESSAGE
        = "BEWARE: Swing update called from non-AWT thread\n"
        + "Change code to use EvenetQueue.invokeLater() or invokeAndWait()";
    
    // Define a pointcut to capture calls to TableModel and its subclass's
    // method that would update the UI. Note, this list is incomplete.
    pointcut swingTableUpdateCalls() : 
           call(* javax..*TableModel+.set*(..))
        || call(* javax..*TableModel+.add*(..))
        || call(* javax..*TableModel+.remove*(..));
    
    // Define a pointcut to capture calls to ListModel and its subclass's
    // method that would update the UI. Note, this list is incomplete.
    pointcut swingListUpdateCalls() :
           call(* javax..*ListModel+.set*(..))
        || call(* javax..*ListModel+.insert*(..));
    // Pointcuts for tree, text, and so on models
    // Implementation left out...
    // Pointcut for updates to any Swing model (use || to add pointcuts
    // to be defined above for tree, text, and so on).
    pointcut swingUpdateCalls() 
        : swingTableUpdateCalls() || swingListUpdateCalls();
    /**
     * Advice to print a warning and stack trace that led to a call
     * to Swing update methods from nonevent dispatch thread.
     */
    before() : swingUpdateCalls() && !if(EventQueue.isDispatchThread()) {
        
        System.err.println(WARNING_MESSAGE);
        System.err.println("Joinpoint captured: " + thisJoinPoint);
        Thread.dumpStack();
        System.err.println();
    }
}

Example 2: Authentication

The next example, found in AbstratcAuthenticationAspect.java, illustrates abstract pointcuts and abstract aspects, as well as crosscutting multiple methods. Other classes are available in DatabaseServer.java, DatabaseClient.java, Authenticator.java, and Test.java:

// AbstratcAuthenticationAspect.java
public abstract aspect AbstractAuthenticationAspect {
    public abstract pointcut operationsNeeddingAuthentication();
    before() : operationsNeeddingAuthentication() {
        // Perform authentication. If it could not be authenticated,
        // let the exception thrown by authenticate propagate.
        Authenticator.authenticate();
    }
}

DatabaseAuthenticationAspect extends the abstract AbstractAuthenticationAspect aspect. It specifies the definition for the operationsNeeddingAuthentication() pointcut capturing operations needing authentication -- in our case calls to the DatabaseServer.connect() method:

// DatabaseAuthenticationAspect.java
public aspect DatabaseAuthenticationAspect 
    extends AbstractAuthenticationAspect {
    public pointcut operationsNeeddingAuthentication():
        call(* DatabaseServer.connect());
}

Example 3: Add Japanese manners to HelloWorld

Now that you've seen the around() advice and collecting context, let's use it to add manners, Japanese style, by adding a "-san" suffix to each person's name to show respect. We simply add another aspect: JapaneseMannersAspect. This aspect advises the call to HelloWorld.sayToPerson(String, String) and proceeds with a modified person argument by adding "-san":

// JapaneseMannersAspect.java
public aspect JapaneseMannersAspect {
    pointcut callSayMessageToPerson(String person) 
        : call(* HelloWorld.sayToPerson(String, String))
               && args(*, person);
    void around(String person) 
        : callSayMessageToPerson(person) {
        proceed(person + "-san");
    }
}

Type-modification constructs

Sometimes it is necessary to affect the static structure in a crosscutting manner, in contrast to affecting dynamic behavior using an advice. AspectJ allows several static-crosscutting types: introduction of new methods and fields, introduction of supertypes, and alteration of a method's exception specification.

Method and field introduction

AspectJ lets you introduce methods or fields to classes and interfaces. Interestingly, AspectJ lets you introduce method implementations and fields into interfaces. You can, for example, provide a default implementation of a few methods in an interface.

The following aspect introduces a foo() method and an instanceCount field of int type in MyClass:

aspect IntroduceMethodIllustration {
        private void MyClass.foo() { 
                System.out.println("This is foo");
        }
        private static int MyClass.instanceCount = 0;
}

Inheritance hierarchy restructuring

With AspectJ, you can modify an existing class's inheritance hierarchy. Using these constructs, you can declare super classes and interfaces of an existing class or interface.

The following aspect declares that MyClass is a Serializable:

aspect MakeMyClassSerializable {
        declare parents : MyClass implements Serializable;
}

Soften an exception

AspectJ can soften a checked exception into an unchecked exception for a particular method set. Such methods then behave as if they've thrown an unchecked exception of type org.aspectj.lang.SoftException, which wraps the original exception.

The next aspect example softens a RemoteException thrown by a call to any public method in DateFetcher's subclasses or DataFetcherImpl's constructor if such a call is made from within Test class's code:

aspect SoftenDateFetcherRemoteException {
        declare soft : 
            RemoteException : (call(public * DateFetcher.*(..))
                               || call(public DateFetcherImpl.new(..)))
                              && within(Test);
}

Note: Since such a declaration affects compile-time behavior, the pointcut must be statically determinable.

The rest in short

In this section, I briefly cover additional AspectJ features:

Related:
1 2 3 Page 2
Page 2 of 3