Aspects

An aspect is a crosscutting type defined by the aspect declaration.

Aspect Declaration

The aspect declaration is similar to the class declaration in that it defines a type and an implementation for that type. It differs in a number of ways:

Aspect implementation can cut across other types

In addition to normal Java class declarations such as methods and fields, aspect declarations can include AspectJ declarations such as advice, pointcuts, and inter-type declarations. Thus, aspects contain implementation declarations that can can cut across other types (including those defined by other aspect declarations).

Aspects are not directly instantiated

Aspects are not directly instantiated with a new expression, with cloning, or with serialization. Aspects may have one constructor definition, but if so it must be of a constructor taking no arguments and throwing no checked exceptions.

Nested aspects must be static

Aspects may be defined either at the package level, or as a static nested aspect -- that is, a static member of a class, interface, or aspect. If it is not at the package level, the aspect must be defined with the static keyword. Local and anonymous aspects are not allowed.

Aspect Extension

To support abstraction and composition of crosscutting concerns, aspects can be extended in much the same way that classes can. Aspect extension adds some new rules, though.

Aspects may extend classes and implement interfaces

An aspect, abstract or concrete, may extend a class and may implement a set of interfaces. Extending a class does not provide the ability to instantiate the aspect with a new expression: The aspect may still only define a null constructor.

Classes may not extend aspects

It is an error for a class to extend or implement an aspect.

Aspects extending aspects

Aspects may extend other aspects, in which case not only are fields and methods inherited but so are pointcuts. However, aspects may only extend abstract aspects. It is an error for a concrete aspect to extend another concrete aspect.

Aspect instantiation

Unlike class expressions, aspects are not instantiated with new expressions. Rather, aspect instances are automatically created to cut across programs.

Because advice only runs in the context of an aspect instance, aspect instantiation indirectly controls when advice runs.

The criteria used to determine how an aspect is instantiated is inherited from its parent aspect. If the aspect has no parent aspect, then by default the aspect is a singleton aspect.

Singleton Aspects

  • aspect Id { ... }
  • aspect Id issingleton() { ... }

By default (or by using the modifier issingleton()) an aspect has exactly one instance that cuts across the entire program. That instance is available at any time during program execution with the static method aspectOf() defined on the aspect -- so, in the above examples, A.aspectOf() will return A's instance. This aspect instance is created as the aspect's classfile is loaded.

Because the an instance of the aspect exists at all join points in the running of a program (once its class is loaded), its advice will have a chance to run at all such join points.

(In actuality, one instance of the aspect A is made for each version of the aspect A, so there will be one instantiation for each time A is loaded by a different classloader.)

Per-object aspects

  • aspect Id perthis(Pointcut) { ... }
  • aspect Id pertarget(Pointcut) { ... }

If an aspect A is defined perthis(Pointcut), then one object of type A is created for every object that is the executing object (i.e., "this") at any of the join points picked out by Pointcut. The advice defined in A may then run at any join point where the currently executing object has been associated with an instance of A.

Similarly, if an aspect A is defined pertarget(Pointcut), then one object of type A is created for every object that is the target object of the join points picked out by Pointcut. The advice defined in A may then run at any join point where the target object has been associated with an instance of A.

In either case, the static method call A.aspectOf(Object) can be used to get the aspect instance (of type A) registered with the object. Each aspect instance is created as early as possible, but not before reaching a join point picked out by Pointcut where there is no associated aspect of type A.

Both perthis and pertarget aspects may be affected by code the AspectJ compiler controls, as discussed in the Implementation Notes appendix.

Per-control-flow aspects

  • aspect Id percflow(Pointcut) { ... }
  • aspect Id percflowbelow(Pointcut) { ... }

If an aspect A is defined percflow(Pointcut) or percflowbelow(Pointcut), then one object of type A is created for each flow of control of the join points picked out by Pointcut, either as the flow of control is entered, or below the flow of control, respectively. The advice defined in A may run at any join point in or under that control flow. During each such flow of control, the static method A.aspectOf() will return an object of type A. An instance of the aspect is created upon entry into each such control flow.

Aspect instantiation and advice

All advice runs in the context of an aspect instance, but it is possible to write a piece of advice with a pointcut that picks out a join point that must occur before asopect instantiation. For example:

  public class Client
  {
      public static void main(String[] args) {
          Client c = new Client();
      }
  }

  aspect Watchcall {
      pointcut myConstructor(): execution(new(..));

      before(): myConstructor() {
          System.err.println("Entering Constructor");
      }
  }

The before advice should run before the execution of all constructors in the system. It must run in the context of an instance of the Watchcall aspect. The only way to get such an instance is to have Watchcall's default constructor execute. But before that executes, we need to run the before advice...

There is no general way to detect these kinds of circularities at compile time. If advice runs before its aspect is instantiated, AspectJ will throw a org.aspectj.lang.NoAspectBoundException.

Aspect privilege

  • privileged aspect Id { ... }

Code written in aspects is subject to the same access control rules as Java code when referring to members of classes or aspects. So, for example, code written in an aspect may not refer to members with default (package-protected) visibility unless the aspect is defined in the same package.

While these restrictions are suitable for many aspects, there may be some aspects in which advice or inter-type members needs to access private or protected resources of other types. To allow this, aspects may be declared privileged. Code in priviliged aspects has access to all members, even private ones.

  class C {
      private int i = 0;
      void incI(int x) { i = i+x; }
  }
  privileged aspect A {
      static final int MAX = 1000;
      before(int x, C c): call(void C.incI(int)) && target(c) && args(x) {
	  if (c.i+x > MAX) throw new RuntimeException();
      }
  }

In this case, if A had not been declared privileged, the field reference c.i would have resulted in an error signaled by the compiler.

If a privileged aspect can access multiple versions of a particular member, then those that it could see if it were not privileged take precedence. For example, in the code

  class C {
      private int i = 0;
      void foo() { }
  }
  privileged aspect A {
      private int C.i = 999;
      before(C c): call(void C.foo()) target(c) {
	  System.out.println(c.i);
      }
  }

A's private inter-type field C.i, initially bound to 999, will be referenced in the body of the advice in preference to C's privately declared field, since the A would have access to its own inter-type fields even if it were not privileged.

Note that a privileged aspect can access private inter-type declarations made by other aspects, since they are simply considered private members of that other aspect.