Notes on AspectJ

David Greenfieldboyce

CMSC 631

What problem is AspectJ trying to solve?

The usual approach to writing complicated computer programs is to break the problem into smaller and smaller pieces, write each of these pieces as a separate module, typically as a function or class, and then compose the pieces into our larger program. The idea of Aspect-oriented programming is that, while modularity is a good approach in general, there are concerns that are more naturally understood as existing across the boundaries of our separate components and that it would be better if the code to implement each of these concerns could be located in a single place instead of being scattered across a bunch of different places in our program. These are called crosscutting concerns or aspects.

It might be reasonable to say that these concerns tend to be things that are ancillary to the central goal of our program and therefore are kind of annoying. Some examples that have been given of  such concerns include:

  • Security checks
  • Logging

  • Boundary and error checking and failure recovery

  • Persistence

  • Synchronization

  • Real-time constraints

Kiczales goes into the problem of crosscutting concerns in more detail in his 1997 paper, "Aspect-Oriented Programming". Hürsch, Lopes (1995) "Separation of Concerns" and Rugaber (1995) "The Interleaving Problem in Program Understanding" give interesting background on the problem of code entanglement without using the aspect framework.

A high level description of the AspectJ approach

Normally, if we have some code that needs to sit in many different places around our program, we would put it in a function and call this function from each place the code is needed. Maybe we would group a bunch of functions that apply to a similar concern into a class, like a security manager, for example.

AspectJ takes largely the same approach. The functions are called "advice". Several pieces of advice can be grouped into a single object, called an "aspect", which is very similar in structure to a class.

Where the approaches diverge is that in our traditional approach we will need to scatter code around our components to call our concern-handling functions at the right times, plus maybe some control-flow that depends on the return values of our functions - a sort of "push" approach to program flow. AspectJ, on the other hand, takes more of a "pull" approach. The places at which the advice functions should be run are specified within the aspect "class", either explicitly through textual specification or more abstractly through a fairly rich set of specifiers using textual wildcards, type specifications, or data flow information. Locations within the program where advice may be applied are called "join points." Complete specifications of where to apply advice may include boolean combinations of join points and are called "pointcuts".

Why might this be better? Even if you have to rely on explicitly listing all of the places where the advice should be applied, it may be an improvement in that 1) you can look in a single location and see everywhere that the advice will be applied. And 2) the code in the various components will be simplified - all of the aspect handling code has been removed.

The situation becomes more compelling when richer pointcut specifications are used. If we can specify our pointcuts in broad, general terms we can have the advice applied automatically in many different locations through a single specification. Besides saving a lot of typing, this has the advantages that as our program grows the advice will be applied automatically in new locations and that we can be sure we're calling the aspect code at all of the right points.

And maybe some of the wrong places. The chief danger of this approach seems to be that program flow is less explicit and so the aspect code may be invoked in places where we would not want it or not invoked in places where we would. This issue is well-addressed by the development tools that have been developed along with AspectJ. These perform two primary functions: at any point in the component code you can see what advice will be applied and at any point in the aspect code you can see to what points in the component code it will be applied. It seems that this is enough information that a programmer can easily follow the code. (There are seemingly some un-fixable limits in that some of the pointcut specifiers are only determinable at runtime, including the instanceOf and cflow specifiers. This ambiguity is analogous to the ambiguity of abstract method calls and probably would not be too confusing.)

Some language specifics

As I've said above, I think AspectJ can be thought of as quite similar to traditional programming techniques except in the way advice functions are invoked. If this is the case, the key to understanding AspectJ programming is in understanding how pointcuts are specified and how advice is applied to pointcuts.

The AspectJ language includes a rich set of join point specifiers. Join points a portions of code, in some cases at a finer granularity than statements, where:

·        fields are set or read

·        methods are invoked

·        exception handlers are invoked

·        initializers are invoked

In addition there are two broader specifiers which apply at all join points where conditions are met - either that the current object is of a particular type or that the join point is reached within an execution flow, such as a method invocation, that can itself be specified as a join point.

Each type of join point specifier requires signature information which can narrow down the set of join points specified. For example, with a field set you can specify the type of the field, the type of the object that contains the field, and the name of the field. Textual wildcarding is allowed in each part of the signature, so each restriction can be completely or partially relaxed.

The details of join point specifiers are listed in table 2 of the paper.

Join points can be combined with AND, OR, and NOT to make pointcuts, which may be more general or more specific than individual join point specifiers can be.

Advice functions are specified to be applied at particular pointcuts. They may be specified to be applied before the join point code is executed, after the join point code is executed, or "around" the join point code. The around designation means that the advice code is applied before and potentially in place of the join point code. That is, the advice code can determine if the join point code will be executed at all. The advice code can also return values, when appropriate, that may be used in place of the evaluation of the join point expression.

Some reflection and parameter passing is provide in the aspect code to allow it to make use of the program context from which it is invoked. For example, the aspect code can determine the method from within which it was invoked.

Since more than one piece of advice may apply at a single join point, AspectJ specifies and elaborate, but seemingly reasonably intuitive, scheme for ordering the application of different advice functions.

A simple example - boundary checking in data structures

To get a feeling for AspectJ, I have been trying to put together some original code to handle a simple aspect. I'm still trying to get it to run but the general design may be instructive.

I am writing a series of data structure classes - a stack, a list, and a heap - which all provide push() and pop() methods. Instead of doing underflow and overflow checks within the methods of each class, I would like to have an aspect which includes two pieces of advice that are applied when the push and pop methods are invoked on the classes that support them. These advice will take care of the necessary checks that would otherwise be in the push and pop methods.

This turns out to be quite simple in AspectJ - the problems I've been having are in setting up the tools. Hopefully I can overcome these problems in time to look at the code in class.

Will it catch on?

AspectJ makes pretty good sense to me. Specifying pointcuts takes some learning, but designing programs to take advantage of aspects has so far been fairly intuitive and I've been impressed by its power in a number of applications. The well-designed development tools will certainly make it more attractive to programmers. The authors make a pretty good case that there's no significant loss in efficiency using AspectJ. Still, programmers are lazy and it may be hard to get people to take the time to learn to program this way.