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.
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.
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.
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.
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.
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 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.)
aspect Id
perthis(Pointcut
) { ... }
aspect Id
pertarget(Pointcut
) { ... }
If an aspect A is defined
perthis(
, 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
)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(
,
then one object of type A is created for every object that is the
target object of the join points picked out by
Pointcut
)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.
aspect Id
percflow(Pointcut
) { ... }
aspect Id
percflowbelow(Pointcut
) { ... }
If an aspect A is defined
percflow(
or
Pointcut
)percflowbelow(
,
then one object of type A is created for each flow of control of the
join points picked out by Pointcut
)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
.
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.