mirror of
https://github.com/eclipse-aspectj/aspectj.git
synced 2024-09-13 15:45:38 +02:00
bcf40bc8dd
* manually applied excellent doc patch contributed by Nick Brett
1310 lines
44 KiB
XML
1310 lines
44 KiB
XML
<chapter id="language" xreflabel="The AspectJ Language">
|
|
|
|
<title>The AspectJ Language</title>
|
|
|
|
<sect1 id="language-intro">
|
|
<title>Introduction</title>
|
|
|
|
<para>
|
|
The previous chapter, <xref linkend="starting" />, was a brief
|
|
overview of the AspectJ language. You should read this chapter to
|
|
understand AspectJ's syntax and semantics. It covers the same
|
|
material as the previous chapter, but more completely and in much
|
|
more detail.
|
|
</para>
|
|
|
|
<para>
|
|
We will start out by looking at an example aspect that we'll build
|
|
out of a pointcut, an introduction, and two pieces of advice. This
|
|
example aspect will gives us something concrete to talk about.
|
|
</para>
|
|
</sect1>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect1 id="language-anatomy">
|
|
<title>The Anatomy of an Aspect</title>
|
|
|
|
<para>
|
|
This lesson explains the parts of AspectJ's aspects. By reading this
|
|
lesson you will have an overview of what's in an aspect and you will
|
|
be exposed to the new terminology introduced by AspectJ.
|
|
</para>
|
|
|
|
<sect2>
|
|
<title>An Example Aspect</title>
|
|
|
|
<para>
|
|
Here's an example of an aspect definition in AspectJ:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
1 aspect FaultHandler {
|
|
2
|
|
3 private boolean Server.disabled = false;
|
|
4
|
|
5 private void reportFault() {
|
|
6 System.out.println("Failure! Please fix it.");
|
|
7 }
|
|
8
|
|
9 public static void fixServer(Server s) {
|
|
10 s.disabled = false;
|
|
11 }
|
|
12
|
|
13 pointcut services(Server s): target(s) && call(public * *(..));
|
|
14
|
|
15 before(Server s): services(s) {
|
|
16 if (s.disabled) throw new DisabledException();
|
|
17 }
|
|
18
|
|
19 after(Server s) throwing (FaultException e): services(s) {
|
|
20 s.disabled = true;
|
|
21 reportFault();
|
|
22 }
|
|
23 }
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
The <literal>FaultHandler</literal> consists of one inter-type
|
|
field on <literal>Server</literal> (line 03), two methods (lines
|
|
05-07 and 09-11), one pointcut definition (line 13), and two pieces
|
|
of advice (lines 15-17 and 19-22).
|
|
</para>
|
|
|
|
<para>
|
|
This covers the basics of what aspects can contain. In general,
|
|
aspects consist of an association of other program entities,
|
|
ordinary variables and methods, pointcut definitions, inter-type declarations,
|
|
and advice, where advice may be before, after or around advice. The
|
|
remainder of this lesson focuses on those crosscut-related
|
|
constructs.
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Pointcuts</title>
|
|
|
|
<para>
|
|
AspectJ's pointcut definitions give names to pointcuts. Pointcuts
|
|
themselves pick out join points, i.e. interesting points in the
|
|
execution of a program. These join points can be method or
|
|
constructor invocations and executions, the handling of exceptions,
|
|
field assignments and accesses, etc. Take, for example, the
|
|
pointcut definition in line 13:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut services(Server s): target(s) && call(public * *(..))
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This pointcut, named <literal>services</literal>, picks out those
|
|
points in the execution of the program when
|
|
<literal>Server</literal> objects have their public methods called.
|
|
It also allows anyone using the <literal>services</literal>
|
|
pointcut to access the <literal>Server</literal> object whose
|
|
method is being called.
|
|
</para>
|
|
|
|
<para>
|
|
The idea behind this pointcut in the
|
|
<literal>FaultHandler</literal> aspect is that
|
|
fault-handling-related behavior must be triggered on the calls to
|
|
public methods. For example, the server may be unable to proceed
|
|
with the request because of some fault. The calls of those methods
|
|
are, therefore, interesting events for this aspect, in the sense
|
|
that certain fault-related things will happen when these events
|
|
occur.
|
|
</para>
|
|
|
|
<para>
|
|
Part of the context in which the events occur is exposed by the
|
|
formal parameters of the pointcut. In this case, that consists of
|
|
objects of type <literal>Server</literal>. That formal parameter
|
|
is then being used on the right hand side of the declaration in
|
|
order to identify which events the pointcut refers to. In this
|
|
case, a pointcut picking out join points where a Server is the
|
|
target of some operation (target(s)) is being composed
|
|
(<literal><![CDATA[&&]]></literal>, meaning and) with a pointcut
|
|
picking out call join points (call(...)). The calls are identified
|
|
by signatures that can include wild cards. In this case, there are
|
|
wild cards in the return type position (first *), in the name
|
|
position (second *) and in the argument list position (..); the
|
|
only concrete information is given by the qualifier
|
|
<literal>public</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Pointcuts pick out arbitrarily large numbers of join points of a
|
|
program. But they pick out only a small number of
|
|
<emphasis>kinds</emphasis> of join points. Those kinds of join
|
|
points correspond to some of the most important concepts in
|
|
Java. Here is an incomplete list: method call, method execution,
|
|
exception handling, instantiation, constructor execution, and
|
|
field access. Each kind of join point can be picked out by its
|
|
own specialized pointcut that you will learn about in other parts
|
|
of this guide.
|
|
</para>
|
|
</sect2>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Advice</title>
|
|
|
|
<para>
|
|
A piece of advice brings together a pointcut and a body of code to
|
|
define aspect implementation that runs at join points picked out by
|
|
the pointcut. For example, the advice in lines 15-17 specifies that
|
|
the following piece of code
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
{
|
|
if (s.disabled) throw new DisabledException();
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
is executed when instances of the <literal>Server</literal> class
|
|
have their public methods called, as specified by the pointcut
|
|
<literal>services</literal>. More specifically, it runs when those
|
|
calls are made, just before the corresponding methods are executed.
|
|
</para>
|
|
|
|
<para>
|
|
The advice in lines 19-22 defines another piece of implementation
|
|
that is executed on the same pointcut:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
{
|
|
s.disabled = true;
|
|
reportFault();
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
But this second method executes after those operations throw
|
|
exception of type <literal>FaultException</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
There are two other variations of after advice: upon successful
|
|
return and upon return, either successful or with an exception.
|
|
There is also a third kind of advice called around. You will see
|
|
those in other parts of this guide.
|
|
</para>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect1 id="language-joinPoints">
|
|
<title>Join Points and Pointcuts</title>
|
|
|
|
<para>
|
|
Consider the following Java class:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
class Point {
|
|
private int x, y;
|
|
|
|
Point(int x, int y) { this.x = x; this.y = y; }
|
|
|
|
void setX(int x) { this.x = x; }
|
|
void setY(int y) { this.y = y; }
|
|
|
|
int getX() { return x; }
|
|
int getY() { return y; }
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
In order to get an intuitive understanding of AspectJ's join points
|
|
and pointcuts, let's go back to some of the basic principles of
|
|
Java. Consider the following a method declaration in class Point:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
void setX(int x) { this.x = x; }
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This piece of program says that that when method named
|
|
<literal>setX</literal> with an <literal>int</literal> argument
|
|
called on an object of type <literal>Point</literal>, then the method
|
|
body <literal>{ this.x = x; }</literal> is executed. Similarly, the
|
|
constructor of the class states that when an object of type
|
|
<literal>Point</literal> is instantiated through a constructor with
|
|
two <literal>int</literal> arguments, then the constructor body
|
|
<literal>{ this.x = x; this.y = y; }</literal> is executed.
|
|
</para>
|
|
|
|
<para>
|
|
One pattern that emerges from these descriptions is
|
|
|
|
<blockquote>
|
|
When something happens, then something gets executed.
|
|
</blockquote>
|
|
|
|
In object-oriented programs, there are several kinds of "things that
|
|
happen" that are determined by the language. We call these the join
|
|
points of Java. Join points consist of things like method calls,
|
|
method executions, object instantiations, constructor executions,
|
|
field references and handler executions. (See the <xref
|
|
linkend="quick" /> for a complete listing.)
|
|
</para>
|
|
|
|
<para>
|
|
Pointcuts pick out these join points. For example, the pointcut
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut setter(): target(Point) &&
|
|
(call(void setX(int)) ||
|
|
call(void setY(int)));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
picks out each call to <literal>setX(int)</literal> or
|
|
<literal>setY(int)</literal> when called on an instance of
|
|
<literal>Point</literal>. Here's another example:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut ioHandler(): within(MyClass) && handler(IOException);
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This pointcut picks out each the join point when exceptions of type
|
|
<literal>IOException</literal> are handled inside the code defined by
|
|
class <literal>MyClass</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Pointcut definitions consist of a left-hand side and a right-hand side,
|
|
separated by a colon. The left-hand side consists of the pointcut name
|
|
and the pointcut parameters (i.e. the data available when the events
|
|
happen). The right-hand side consists of the pointcut itself.
|
|
</para>
|
|
|
|
<sect2>
|
|
<title>Some Example Pointcuts</title>
|
|
|
|
<para>
|
|
Here are examples of pointcuts picking out
|
|
</para>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>when a particular method body executes</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>execution(void Point.setX(int))</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>when a method is called</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>call(void Point.setX(int))</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>when an exception handler executes</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>handler(ArrayOutOfBoundsException)</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>
|
|
when the object currently executing
|
|
(i.e. <literal>this</literal>) is of type
|
|
<literal>SomeType</literal>
|
|
</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>this(SomeType)</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>
|
|
when the target object is of type <literal>SomeType</literal>
|
|
</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>target(SomeType)</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>
|
|
when the executing code belongs to
|
|
class <literal>MyClass</literal>
|
|
</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>within(MyClass)</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>
|
|
when the join point is in the control flow of a call to a
|
|
<literal>Test</literal>'s no-argument <literal>main</literal>
|
|
method
|
|
</term>
|
|
<listitem>
|
|
<para>
|
|
<literal>cflow(call(void Test.main()))</literal>
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
|
|
<para>
|
|
Pointcuts compose through the operations <literal>or</literal>
|
|
("<literal>||</literal>"), <literal>and</literal>
|
|
("<literal><![CDATA[&&]]></literal>") and <literal>not</literal>
|
|
("<literal>!</literal>").
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
It is possible to use wildcards. So
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
<literal>execution(* *(..))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>call(* set(..))</literal>
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
means (1) the execution of any method regardless of return or
|
|
parameter types, and (2) the call to any method named
|
|
<literal>set</literal> regardless of return or parameter types
|
|
-- in case of overloading there may be more than one such
|
|
<literal>set</literal> method; this pointcut picks out calls to
|
|
all of them.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
You can select elements based on types. For example,
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
<literal>execution(int *())</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>call(* setY(long))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>call(* Point.setY(int))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>call(*.new(int, int))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
means (1) the execution of any method with no parameters that
|
|
returns an <literal>int</literal>, (2) the call to any
|
|
<literal>setY</literal> method that takes a
|
|
<literal>long</literal> as an argument, regardless of return
|
|
type or declaring type, (3) the call to any of
|
|
<literal>Point</literal>'s <literal>setY</literal> methods that
|
|
take an <literal>int</literal> as an argument, regardless of
|
|
return type, and (4) the call to any classes' constructor, so
|
|
long as it takes exactly two <literal>int</literal>s as
|
|
arguments.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
You can compose pointcuts. For example,
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
<literal>target(Point) <![CDATA[&&]]> call(int *())</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>call(* *(..)) <![CDATA[&&]]> (within(Line) || within(Point))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>within(*) <![CDATA[&&]]> execution(*.new(int))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>
|
|
!this(Point) <![CDATA[&&]]> call(int *(..))
|
|
</literal>
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
means (1) any call to an <literal>int</literal> method with no
|
|
arguments on an instance of <literal>Point</literal>,
|
|
regardless of its name, (2) any call to any method where the
|
|
call is made from the code in <literal>Point</literal>'s or
|
|
<literal>Line</literal>'s type declaration, (3) the execution of
|
|
any constructor taking exactly one <literal>int</literal>
|
|
argument, regardless of where the call is made from, and
|
|
(4) any method call to an <literal>int</literal> method when
|
|
the executing object is any type except <literal>Point</literal>.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
You can select methods and constructors based on their
|
|
modifiers and on negations of modifiers. For example, you can
|
|
say:
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
<literal>call(public * *(..))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal>execution(!static * *(..))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<literal> execution(public !static * *(..))</literal>
|
|
</para>
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
which means (1) any call to a public method, (2) any
|
|
execution of a non-static method, and (3) any execution of a
|
|
public, non-static method.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Pointcuts can also deal with interfaces. For example, given the
|
|
interface </para>
|
|
|
|
<programlisting><![CDATA[
|
|
interface MyInterface { ... }
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
the pointcut <literal>call(* MyInterface.*(..))</literal> picks
|
|
out any call to a method in <literal>MyInterface</literal>'s
|
|
signature -- that is, any method defined by
|
|
<literal>MyInterface</literal> or inherited by one of its a
|
|
supertypes.
|
|
</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>call vs. execution</title>
|
|
|
|
<para>
|
|
When methods and constructors run, there are two interesting times
|
|
associated with them. That is when they are called, and when they
|
|
actually execute.
|
|
</para>
|
|
|
|
<para>
|
|
AspectJ exposes these times as call and execution join points,
|
|
respectively, and allows them to be picked out specifically by
|
|
<literal>call</literal> and <literal>execution</literal> pointcuts.
|
|
</para>
|
|
|
|
<para>
|
|
So what's the difference between these join points? Well, there are a
|
|
number of differences:
|
|
</para>
|
|
|
|
<para>
|
|
Firstly, the lexical pointcut declarations
|
|
<literal>within</literal> and <literal>withincode</literal> match
|
|
differently. At a call join point, the enclosing code is that of
|
|
the call site. This means that <literal>call(void m())
|
|
<![CDATA[&&]]> withincode(void m())</literal> will only capture
|
|
directly recursive calls, for example. At an execution join point,
|
|
however, the program is already executing the method, so the
|
|
enclosing code is the method itself: <literal>execution(void m())
|
|
<![CDATA[&&]]> withincode(void m())</literal> is the same as
|
|
<literal>execution(void m())</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Secondly, the call join point does not capture super calls to
|
|
non-static methods. This is because such super calls are different in
|
|
Java, since they don't behave via dynamic dispatch like other calls to
|
|
non-static methods.
|
|
</para>
|
|
|
|
<para>
|
|
The rule of thumb is that if you want to pick a join point that
|
|
runs when an actual piece of code runs (as is often the case for
|
|
tracing), use <literal>execution</literal>, but if you want to pick
|
|
one that runs when a particular <emphasis>signature</emphasis> is
|
|
called (as is often the case for production aspects), use
|
|
<literal>call</literal>.
|
|
</para>
|
|
</sect2>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Pointcut composition</title>
|
|
|
|
<para>
|
|
Pointcuts are put together with the operators and (spelled
|
|
<literal>&&</literal>), or (spelled <literal>||</literal>),
|
|
and not (spelled <literal>!</literal>). This allows the creation
|
|
of very powerful pointcuts from the simple building blocks of
|
|
primitive pointcuts. This composition can be somewhat confusing
|
|
when used with primitive pointcuts like <literal>cflow</literal>
|
|
and <literal>cflowbelow</literal>. Here's an example:
|
|
</para>
|
|
|
|
<para>
|
|
<literal>cflow(<replaceable>P</replaceable>)</literal> picks out
|
|
each join point in the control flow of the join points picked out
|
|
by <replaceable>P</replaceable>. So, pictorially:
|
|
</para>
|
|
|
|
<programlisting>
|
|
P ---------------------
|
|
\
|
|
\ cflow of P
|
|
\
|
|
</programlisting>
|
|
|
|
|
|
<para>
|
|
What does <literal>cflow(<replaceable>P</replaceable>) &&
|
|
cflow(<replaceable>Q</replaceable>)</literal> pick out? Well, it
|
|
picks out each join point that is in both the control flow of
|
|
<replaceable>P</replaceable> and in the control flow of
|
|
<replaceable>Q</replaceable>. So...
|
|
</para>
|
|
|
|
<programlisting>
|
|
P ---------------------
|
|
\
|
|
\ cflow of P
|
|
\
|
|
\
|
|
\
|
|
Q -------------\-------
|
|
\ \
|
|
\ cflow of Q \ cflow(P) && cflow(Q)
|
|
\ \
|
|
</programlisting>
|
|
|
|
<para>
|
|
Note that <replaceable>P</replaceable> and
|
|
<replaceable>Q</replaceable> might not have any join points in
|
|
common... but their control flows might have join points in common.
|
|
</para>
|
|
|
|
<para>
|
|
But what does <literal>cflow(<replaceable>P</replaceable>
|
|
&& <replaceable>Q</replaceable>)</literal> mean? Well, it
|
|
means the control flow of those join points that are both picked
|
|
out by <replaceable>P</replaceable> and picked out by
|
|
<replaceable>Q</replaceable>.
|
|
</para>
|
|
|
|
<programlisting>
|
|
P && Q -------------------
|
|
\
|
|
\ cflow of (P && Q)
|
|
\
|
|
</programlisting>
|
|
|
|
<para>
|
|
and if there are <emphasis>no</emphasis> join points that are both
|
|
picked by <replaceable>P</replaceable> and picked out by
|
|
<replaceable>Q</replaceable>, then there's no chance that there are
|
|
any join points in the control flow of
|
|
<literal>(<replaceable>P</replaceable> &&
|
|
<replaceable>Q</replaceable>)</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Here's some code that expresses this.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
public class Test {
|
|
public static void main(String[] args) {
|
|
foo();
|
|
}
|
|
static void foo() {
|
|
goo();
|
|
}
|
|
static void goo() {
|
|
System.out.println("hi");
|
|
}
|
|
}
|
|
|
|
aspect A {
|
|
pointcut fooPC(): execution(void Test.foo());
|
|
pointcut gooPC(): execution(void Test.goo());
|
|
pointcut printPC(): call(void java.io.PrintStream.println(String));
|
|
|
|
before(): cflow(fooPC()) && cflow(gooPC()) && printPC() && !within(A) {
|
|
System.out.println("should occur");
|
|
}
|
|
|
|
before(): cflow(fooPC() && gooPC()) && printPC() && !within(A) {
|
|
System.out.println("should not occur");
|
|
}
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
The <literal>!within(<replaceable>A</replaceable>)</literal>
|
|
pointcut above is required to avoid the <literal>printPC</literal>
|
|
pointcut applying to the <literal>System.out.println</literal>
|
|
call in the advice body. If this was not present a recursive call
|
|
would result as the pointcut would apply to it's own advice.
|
|
(See <xref linkend="pitfalls-infiniteLoops"/> for more details.)
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Pointcut Parameters</title>
|
|
|
|
<para>
|
|
Consider again the first pointcut definition in this chapter:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut setter(): target(Point) &&
|
|
(call(void setX(int)) ||
|
|
call(void setY(int)));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
As we've seen, this pointcut picks out each call to
|
|
<literal>setX(int)</literal> or <literal>setY(int)</literal>
|
|
methods where the target is an instance of
|
|
<literal>Point</literal>. The pointcut is given the name
|
|
<literal>setters</literal> and no parameters on the left-hand
|
|
side. An empty parameter list means that none of the context from
|
|
the join points is published from this pointcut. But consider
|
|
another version of version of this pointcut definition:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut setter(Point p): target(p) &&
|
|
(call(void setX(int)) ||
|
|
call(void setY(int)));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This version picks out exactly the same join points. But in this
|
|
version, the pointcut has one parameter of type
|
|
<literal>Point</literal>. This means that any advice that uses this
|
|
pointcut has access to a <literal>Point</literal> from each join
|
|
point picked out by the pointcut. Inside the pointcut definition
|
|
this <literal>Point</literal> is named <literal>p</literal> is
|
|
available, and according to the right-hand side of the definition,
|
|
that <literal>Point p</literal> comes from the
|
|
<literal>target</literal> of each matched join point.
|
|
</para>
|
|
|
|
<para>
|
|
Here's another example that illustrates the flexible mechanism for
|
|
defining pointcut parameters:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut testEquality(Point p): target(Point) &&
|
|
args(p) &&
|
|
call(boolean equals(Object));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This pointcut also has a parameter of type
|
|
<literal>Point</literal>. Similar to the
|
|
<literal>setters</literal> pointcut, this means that anyone using
|
|
this pointcut has access to a <literal>Point</literal> from each
|
|
join point. But in this case, looking at the right-hand side we
|
|
find that the object named in the parameters is not the target
|
|
<literal>Point</literal> object that receives the call; it's the
|
|
argument (also of type <literal>Point</literal>) passed to the
|
|
<literal>equals</literal> method when some other
|
|
<literal>Point</literal> is the target. If we wanted access to both
|
|
<literal>Point</literal>s, then the pointcut definition that would
|
|
expose target <literal>Point p1</literal> and argument
|
|
<literal>Point p2</literal> would be
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut testEquality(Point p1, Point p2): target(p1) &&
|
|
args(p2) &&
|
|
call(boolean equals(Object));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
Let's look at another variation of the <literal>setters</literal> pointcut:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut setter(Point p, int newval): target(p) &&
|
|
args(newval) &&
|
|
(call(void setX(int)) ||
|
|
call(void setY(int)));
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
In this case, a <literal>Point</literal> object and an
|
|
<literal>int</literal> value are exposed by the named
|
|
pointcut. Looking at the the right-hand side of the definition, we
|
|
find that the <literal>Point</literal> object is the target object,
|
|
and the <literal>int</literal> value is the called method's
|
|
argument.
|
|
</para>
|
|
|
|
<para>
|
|
The use of pointcut parameters is relatively flexible. The most
|
|
important rule is that all the pointcut parameters must be bound at
|
|
every join point picked out by the pointcut. So, for example, the
|
|
following pointcut definition will result in a compilation error:
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut badPointcut(Point p1, Point p2):
|
|
(target(p1) && call(void setX(int))) ||
|
|
(target(p2) && call(void setY(int)));
|
|
]]></programlisting>
|
|
|
|
because <literal>p1</literal> is only bound when calling
|
|
<literal>setX</literal>, and <literal>p2</literal> is only bound
|
|
when calling <literal>setY</literal>, but the pointcut picks out
|
|
all of these join points and tries to bind both
|
|
<literal>p1</literal> and <literal>p2</literal>.
|
|
</para>
|
|
</sect2>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Example: <literal>HandleLiveness</literal></title>
|
|
|
|
<para>
|
|
The example below consists of two object classes (plus an exception
|
|
class) and one aspect. Handle objects delegate their public,
|
|
non-static operations to their <literal>Partner</literal>
|
|
objects. The aspect <literal>HandleLiveness</literal> ensures that,
|
|
before the delegations, the partner exists and is alive, or else it
|
|
throws an exception.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
class Handle {
|
|
Partner partner = new Partner();
|
|
|
|
public void foo() { partner.foo(); }
|
|
public void bar(int x) { partner.bar(x); }
|
|
|
|
public static void main(String[] args) {
|
|
Handle h1 = new Handle();
|
|
h1.foo();
|
|
h1.bar(2);
|
|
}
|
|
}
|
|
|
|
class Partner {
|
|
boolean isAlive() { return true; }
|
|
void foo() { System.out.println("foo"); }
|
|
void bar(int x) { System.out.println("bar " + x); }
|
|
}
|
|
|
|
aspect HandleLiveness {
|
|
before(Handle handle): target(handle) && call(public * *(..)) {
|
|
if ( handle.partner == null || !handle.partner.isAlive() ) {
|
|
throw new DeadPartnerException();
|
|
}
|
|
}
|
|
}
|
|
|
|
class DeadPartnerException extends RuntimeException {}
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect1 id="language-advice">
|
|
<title>Advice</title>
|
|
|
|
<para>
|
|
Advice defines pieces of aspect implementation that execute at
|
|
well-defined points in the execution of the program. Those points can
|
|
be given either by named pointcuts (like the ones you've seen above)
|
|
or by anonymous pointcuts. Here is an example of an advice on a named
|
|
pointcut:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
pointcut setter(Point p1, int newval): target(p1) && args(newval)
|
|
(call(void setX(int) ||
|
|
call(void setY(int)));
|
|
|
|
before(Point p1, int newval): setter(p1, newval) {
|
|
System.out.println("About to set something in " + p1 +
|
|
" to the new value " + newval);
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
And here is exactly the same example, but using an anonymous
|
|
pointcut:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
before(Point p1, int newval): target(p1) && args(newval)
|
|
(call(void setX(int)) ||
|
|
call(void setY(int))) {
|
|
System.out.println("About to set something in " + p1 +
|
|
" to the new value " + newval);
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
Here are examples of the different advice:
|
|
</para>
|
|
|
|
<para>
|
|
This before advice runs just before the join points picked out by the
|
|
(anonymous) pointcut:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
before(Point p, int x): target(p) && args(x) && call(void setX(int)) {
|
|
if (!p.assertX(x)) return;
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This after advice runs just after each join point picked out by the
|
|
(anonymous) pointcut, regardless of whether it returns normally or throws
|
|
an exception:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
after(Point p, int x): target(p) && args(x) && call(void setX(int)) {
|
|
if (!p.assertX(x)) throw new PostConditionViolation();
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This after returning advice runs just after each join point picked
|
|
out by the (anonymous) pointcut, but only if it returns normally.
|
|
The return value can be accessed, and is named <literal>x</literal>
|
|
here. After the advice runs, the return value is returned:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
after(Point p) returning(int x): target(p) && call(int getX()) {
|
|
System.out.println("Returning int value " + x + " for p = " + p);
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This after throwing advice runs just after each join point picked out by
|
|
the (anonymous) pointcut, but only when it throws an exception of type
|
|
<literal>Exception</literal>. Here the exception value can be accessed
|
|
with the name <literal>e</literal>. The advice re-raises the exception
|
|
after it's done:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
after() throwing(Exception e): target(Point) && call(void setX(int)) {
|
|
System.out.println(e);
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
This around advice traps the execution of the join point; it runs
|
|
<emphasis>instead</emphasis> of the join point. The original action
|
|
associated with the join point can be invoked through the special
|
|
<literal>proceed</literal> call:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
void around(Point p, int x): target(p)
|
|
&& args(x)
|
|
&& call(void setX(int)) {
|
|
if (p.assertX(x)) proceed(p, x);
|
|
p.releaseResources();
|
|
}
|
|
]]></programlisting>
|
|
</sect1>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect1 id="language-interType">
|
|
<title>Inter-type declarations</title>
|
|
|
|
<para>
|
|
Aspects can declare members (fields, methods, and constructors) that
|
|
are owned by other types. These are called inter-type members.
|
|
Aspects can also declare that other types implement new interfaces or
|
|
extend a new class. Here are examples of some such inter-type
|
|
declarations:
|
|
</para>
|
|
|
|
<para>
|
|
This declares that each <literal>Server</literal> has a
|
|
<literal>boolean</literal> field named <literal>disabled</literal>,
|
|
initialized to <literal>false</literal>:
|
|
|
|
<programlisting><![CDATA[
|
|
private boolean Server.disabled = false;
|
|
]]></programlisting>
|
|
|
|
It is declared <literal>private</literal>, which means that it is
|
|
private <emphasis>to the aspect</emphasis>: only code in the aspect
|
|
can see the field. And even if <literal>Server</literal> has
|
|
another private field named <literal>disabled</literal> (declared in
|
|
<literal>Server</literal> or in another aspect) there won't be a name
|
|
collision, since no reference to <literal>disabled</literal> will be
|
|
ambiguous.
|
|
</para>
|
|
|
|
<para>
|
|
This declares that each <literal>Point</literal> has an
|
|
<literal>int</literal> method named <literal>getX</literal> with no
|
|
arguments that returns whatever <literal>this.x</literal> is:
|
|
|
|
<programlisting><![CDATA[
|
|
public int Point.getX() { return this.x; }
|
|
]]></programlisting>
|
|
|
|
Inside the body, <literal>this</literal> is the
|
|
<literal>Point</literal> object currently executing. Because the
|
|
method is publically declared any code can call it, but if there is
|
|
some other <literal>Point.getX()</literal> declared there will be a
|
|
compile-time conflict.
|
|
</para>
|
|
|
|
<para>
|
|
This publically declares a two-argument constructor for
|
|
<literal>Point</literal>:
|
|
|
|
<programlisting><![CDATA[
|
|
public Point.new(int x, int y) { this.x = x; this.y = y; }
|
|
]]></programlisting>
|
|
|
|
</para>
|
|
|
|
<para>
|
|
This publicly declares that each <literal>Point</literal> has an
|
|
<literal>int</literal> field named <literal>x</literal>, initialized
|
|
to zero:
|
|
|
|
<programlisting><![CDATA[
|
|
public int Point.x = 0;
|
|
]]></programlisting>
|
|
|
|
Because this is publically declared, it is an error if
|
|
<literal>Point</literal> already has a field named
|
|
<literal>x</literal> (defined by <literal>Point</literal> or by
|
|
another aspect).
|
|
</para>
|
|
|
|
<para>
|
|
This declares that the <literal>Point</literal> class implements the
|
|
<literal>Comparable</literal> interface:
|
|
|
|
<programlisting><![CDATA[
|
|
declare parents: Point implements Comparable;
|
|
]]></programlisting>
|
|
|
|
Of course, this will be an error unless <literal>Point</literal>
|
|
defines the methods required by <literal>Comparable</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
This declares that the <literal>Point</literal> class extends the
|
|
<literal>GeometricObject</literal> class.
|
|
|
|
<programlisting><![CDATA[
|
|
declare parents: Point extends GeometricObject;
|
|
]]></programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
An aspect can have several inter-type declarations. For example, the
|
|
following declarations
|
|
|
|
<programlisting><![CDATA[
|
|
public String Point.name;
|
|
public void Point.setName(String name) { this.name = name; }
|
|
]]></programlisting>
|
|
|
|
publicly declare that Point has both a String field
|
|
<literal>name</literal> and a <literal>void</literal> method
|
|
<literal>setName(String)</literal> (which refers to the
|
|
<literal>name</literal> field declared by the aspect).
|
|
</para>
|
|
|
|
<para>
|
|
An inter-type member can only have one target type, but often you may
|
|
wish to declare the same member on more than one type. This can be
|
|
done by using an inter-type member in combination with a private
|
|
interface:
|
|
|
|
<programlisting><![CDATA[
|
|
aspect A {
|
|
private interface HasName {}
|
|
declare parents: (Point || Line || Square) implements HasName;
|
|
|
|
private String HasName.name;
|
|
public String HasName.getName() { return name; }
|
|
}
|
|
]]></programlisting>
|
|
|
|
This declares a marker interface <literal>HasName</literal>, and also declares that any
|
|
type that is either <literal>Point</literal>,
|
|
<literal>Line</literal>, or <literal>Square</literal> implements that
|
|
interface. It also privately declares that all <literal>HasName</literal>
|
|
object have a <literal>String</literal> field called
|
|
<literal>name</literal>, and publically declares that all
|
|
<literal>HasName</literal> objects have a <literal>String</literal>
|
|
method <literal>getName()</literal> (which refers to the privately
|
|
declared <literal>name</literal> field).
|
|
</para>
|
|
|
|
<para>
|
|
As you can see from the above example, an aspect can declare that
|
|
interfaces have fields and methods, even non-constant fields and
|
|
methods with bodies.
|
|
</para>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Inter-type Scope</title>
|
|
|
|
<para>
|
|
AspectJ allows private and package-protected (default) inter-type declarations in
|
|
addition to public inter-type declarations. Private means private in
|
|
relation to the aspect, not necessarily the target type. So, if an
|
|
aspect makes a private inter-type declaration of a field
|
|
|
|
<programlisting><![CDATA[
|
|
private int Foo.x;
|
|
]]></programlisting>
|
|
|
|
Then code in the aspect can refer to <literal>Foo</literal>'s
|
|
<literal>x</literal> field, but nobody else can. Similarly, if an
|
|
aspect makes a package-protected introduction,
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
int Foo.x;
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
then everything in the aspect's package (which may or may not be
|
|
<literal>Foo</literal>'s package) can access <literal>x</literal>.
|
|
</para>
|
|
</sect2>
|
|
|
|
<!-- ============================== -->
|
|
|
|
<sect2>
|
|
<title>Example: <literal>PointAssertions</literal></title>
|
|
|
|
<para>
|
|
The example below consists of one class and one aspect. The aspect
|
|
privately declares the assertion methods of
|
|
<literal>Point</literal>, <literal>assertX</literal> and
|
|
<literal>assertY</literal>. It also guards calls to
|
|
<literal>setX</literal> and <literal>setY</literal> with calls to
|
|
these assertion methods. The assertion methods are declared
|
|
privately because other parts of the program (including the code in
|
|
<literal>Point</literal>) have no business accessing the assert
|
|
methods. Only the code inside of the aspect can call those
|
|
methods.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
class Point {
|
|
int x, y;
|
|
|
|
public void setX(int x) { this.x = x; }
|
|
public void setY(int y) { this.y = y; }
|
|
|
|
public static void main(String[] args) {
|
|
Point p = new Point();
|
|
p.setX(3); p.setY(333);
|
|
}
|
|
}
|
|
|
|
aspect PointAssertions {
|
|
|
|
private boolean Point.assertX(int x) {
|
|
return (x <= 100 && x >= 0);
|
|
}
|
|
private boolean Point.assertY(int y) {
|
|
return (y <= 100 && y >= 0);
|
|
}
|
|
|
|
before(Point p, int x): target(p) && args(x) && call(void setX(int)) {
|
|
if (!p.assertX(x)) {
|
|
System.out.println("Illegal value for x"); return;
|
|
}
|
|
}
|
|
before(Point p, int y): target(p) && args(y) && call(void setY(int)) {
|
|
if (!p.assertY(y)) {
|
|
System.out.println("Illegal value for y"); return;
|
|
}
|
|
}
|
|
}
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- ================================================== -->
|
|
|
|
<sect1 id="language-thisJoinPoint">
|
|
<title>thisJoinPoint</title>
|
|
|
|
<para>
|
|
AspectJ provides a special reference variable,
|
|
<literal>thisJoinPoint</literal>, that contains reflective
|
|
information about the current join point for the advice to use. The
|
|
<literal>thisJoinPoint</literal> variable can only be used in the
|
|
context of advice, just like <literal>this</literal> can only be used
|
|
in the context of non-static methods and variable initializers. In
|
|
advice, <literal>thisJoinPoint</literal> is an object of type <ulink
|
|
url="../api/org/aspectj/lang/JoinPoint.html"><literal>org.aspectj.lang.JoinPoint</literal></ulink>.
|
|
</para>
|
|
|
|
<para>
|
|
One way to use it is simply to print it out. Like all Java objects,
|
|
<literal>thisJoinPoint</literal> has a <literal>toString()</literal>
|
|
method that makes quick-and-dirty tracing easy:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
class TraceNonStaticMethods {
|
|
before(Point p): target(p) && call(* *(..)) {
|
|
System.out.println("Entering " + thisJoinPoint + " in " + p);
|
|
}
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
The type of <literal>thisJoinPoint</literal> includes a rich
|
|
reflective class hierarchy of signatures, and can be used to access
|
|
both static and dynamic information about join points such as the
|
|
arguments of the join point:
|
|
|
|
<programlisting><![CDATA[
|
|
thisJoinPoint.getArgs()
|
|
]]></programlisting>
|
|
|
|
In addition, it holds an object consisting of all the static
|
|
information about the join point such as corresponding line number
|
|
and static signature:
|
|
|
|
<programlisting><![CDATA[
|
|
thisJoinPoint.getStaticPart()
|
|
]]></programlisting>
|
|
|
|
If you only need the static information about the join point, you may
|
|
access the static part of the join point directly with the special
|
|
variable <literal>thisJoinPointStaticPart</literal>. Using
|
|
<literal>thisJoinPointStaticPart</literal> will avoid the run-time
|
|
creation of the join point object that may be necessary when using
|
|
<literal>thisJoinPoint</literal> directly.
|
|
</para>
|
|
|
|
<para>It is always the case that
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
thisJoinPointStaticPart == thisJoinPoint.getStaticPart()
|
|
|
|
thisJoinPoint.getKind() == thisJoinPointStaticPart.getKind()
|
|
thisJoinPoint.getSignature() == thisJoinPointStaticPart.getSignature()
|
|
thisJoinPoint.getSourceLocation() == thisJoinPointStaticPart.getSourceLocation()
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
One more reflective variable is available:
|
|
<literal>thisEnclosingJoinPointStaticPart</literal>. This, like
|
|
<literal>thisJoinPointStaticPart</literal>, only holds the static
|
|
part of a join point, but it is not the current but the enclosing
|
|
join point. So, for example, it is possible to print out the calling
|
|
source location (if available) with
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
before() : execution (* *(..)) {
|
|
System.err.println(thisEnclosingJoinPointStaticPart.getSourceLocation())
|
|
}
|
|
]]></programlisting>
|
|
|
|
</sect1>
|
|
</chapter>
|