Aspects

An aspect is a crosscutting type defined by the 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 that the type and implementation can cut across other types (including those defined by other aspect declarations), and that it may not be 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.

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.

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 Limitations 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 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 introductions 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 signalled 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 introduced private 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 fields it introduces even if it were not privileged.

Aspect domination

aspect Id dominates TypePattern { ... }

An aspect may declare that the advice in it dominates the advice in some other aspect. Such declarations are like the strictfp keyword in Java; it applies to the advice declarations inside of the respective aspects, and states that the advice declared in the current aspect has more precedence than the advice in the aspects from TypePattern.

For example, the CountEntry aspect might want to count the entry to methods in the current package accepting a Type object as its first argument. However, it should count all entries, even those that the aspect DisallowNulls causes to throw exceptions. This can be accomplished by stating that CountEntry dominates DisallowNulls.

aspect DisallowNulls {
    pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..);
    before(Type obj):  allTypeMethods(obj) {
        if (obj == null) throw new RuntimeException();
    }
}
aspect CountEntry dominates DisallowNulls {
    pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..);
    static int count = 0;
    before():  allTypeMethods(Type) {
        count++;
    }
}