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 a number of ways:
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 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.
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.
Unlike class expressions, aspects are not instantiated with new expressions. Rather, aspect instances are automatically created to cut across programs. A program can get a reference to an aspect instance using the static method aspectOf(..).
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. How an aspect is instantiated controls the form of the aspectOf(..) method defined on the concrete aspect class.
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 from the static method aspectOf() automatically defined on all concrete aspects -- 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.)
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 will run only at a 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 will run only at a 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.
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.
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.
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.