- <chapter id="starting" xreflabel="Getting Started with AspectJ">
-
- <title>Getting Started with AspectJ</title>
-
- <sect1 id="starting-intro">
- <title>Introduction</title>
-
- <para>
- Many software developers are attracted to the idea of aspect-oriented
- programming (AOP) but unsure about how to begin using the
- technology. They recognize the concept of crosscutting concerns, and
- know that they have had problems with the implementation of such
- concerns in the past. But there are many questions about how to adopt
- AOP into the development process. Common questions include:
-
- <itemizedlist spacing="compact">
- <listitem>
- <para>Can I use aspects in my existing code?</para>
- </listitem>
-
- <listitem>
- <para>
- What kinds of benefits can I expect to get from using aspects?
- </para>
- </listitem>
-
- <listitem>
- <para>How do I find aspects in my programs?</para>
- </listitem>
-
- <listitem>
- <para>How steep is the learning curve for AOP?</para>
- </listitem>
-
- <listitem>
- <para>What are the risks of using this new technology?</para>
- </listitem>
-
- </itemizedlist>
- </para>
-
- <para>
- This chapter addresses these questions in the context of AspectJ: a
- general-purpose aspect-oriented extension to Java. A series of
- abridged examples illustrate the kinds of aspects programmers may
- want to implement using AspectJ and the benefits associated with
- doing so. Readers who would like to understand the examples in more
- detail, or who want to learn how to program examples like these, can
- find more complete examples and supporting material linked from the
- AspectJ web site ( <ulink url="http://eclipse.org/aspectj" /> ).
- </para>
-
- <para>
- A significant risk in adopting any new technology is going too far
- too fast. Concern about this risk causes many organizations to be
- conservative about adopting new technology. To address this issue,
- the examples in this chapter are grouped into three broad categories,
- with aspects that are easier to adopt into existing development
- projects coming earlier in this chapter. The next section, <xref
- linkend="starting-aspectj"/>, we present the core of AspectJ's
- features, and in <xref linkend="starting-development"/>, we present
- aspects that facilitate tasks such as debugging, testing and
- performance tuning of applications. And, in the section following,
- <xref linkend="starting-production"/>, we present aspects that
- implement crosscutting functionality common in Java applications. We
- will defer discussing a third category of aspects, reusable aspects,
- until <xref linkend="language"/>.
- </para>
-
- <para>
- These categories are informal, and this ordering is not the only way
- to adopt AspectJ. Some developers may want to use a production aspect
- right away. But our experience with current AspectJ users suggests
- that this is one ordering that allows developers to get experience
- with (and benefit from) AOP technology quickly, while also minimizing
- risk.
- </para>
- </sect1>
-
- <sect1 id="starting-aspectj" xreflabel="Introduction to AspectJ">
- <title>Introduction to AspectJ</title>
-
- <para>
- This section presents a brief introduction to the features of AspectJ
- used later in this chapter. These features are at the core of the
- language, but this is by no means a complete overview of AspectJ.
- </para>
-
- <para>
- The features are presented using a simple figure editor system. A
- <classname>Figure</classname> consists of a number of
- <classname>FigureElements</classname>, which can be either
- <classname>Point</classname>s or <classname>Line</classname>s. The
- <classname>Figure</classname> class provides factory services. There
- is also a <classname>Display</classname>. Most example programs later
- in this chapter are based on this system as well.
- </para>
-
- <para>
- <mediaobject>
- <imageobject>
- <imagedata fileref="figureUML.gif"/>
- </imageobject>
- <caption>
- <para>
- UML for the <literal>FigureEditor</literal> example
- </para>
- </caption>
- </mediaobject>
- </para>
-
- <para>
- The motivation for AspectJ (and likewise for aspect-oriented
- programming) is the realization that there are issues or concerns
- that are not well captured by traditional programming
- methodologies. Consider the problem of enforcing a security policy in
- some application. By its nature, security cuts across many of the
- natural units of modularity of the application. Moreover, the
- security policy must be uniformly applied to any additions as the
- application evolves. And the security policy that is being applied
- might itself evolve. Capturing concerns like a security policy in a
- disciplined way is difficult and error-prone in a traditional
- programming language.
- </para>
-
- <para>
- Concerns like security cut across the natural units of
- modularity. For object-oriented programming languages, the natural
- unit of modularity is the class. But in object-oriented programming
- languages, crosscutting concerns are not easily turned into classes
- precisely because they cut across classes, and so these aren't
- reusable, they can't be refined or inherited, they are spread through
- out the program in an undisciplined way, in short, they are difficult
- to work with.
- </para>
-
- <para>
- Aspect-oriented programming is a way of modularizing crosscutting
- concerns much like object-oriented programming is a way of
- modularizing common concerns. AspectJ is an implementation of
- aspect-oriented programming for Java.
- </para>
-
- <para>
- AspectJ adds to Java just one new concept, a join point -- and that's
- really just a name for an existing Java concept. It adds to Java
- only a few new constructs: pointcuts, advice, inter-type declarations
- and aspects. Pointcuts and advice dynamically affect program flow,
- inter-type declarations statically affects a program's class
- hierarchy, and aspects encapsulate these new constructs.
- </para>
-
- <para>
- A <emphasis>join point</emphasis> is a well-defined point in the
- program flow. A <emphasis>pointcut</emphasis> picks out certain join
- points and values at those points. A piece of
- <emphasis>advice</emphasis> is code that is executed when a join
- point is reached. These are the dynamic parts of AspectJ.
- </para>
-
- <para>
- AspectJ also has different kinds of <emphasis>inter-type
- declarations</emphasis> that allow the programmer to modify a
- program's static structure, namely, the members of its classes and
- the relationship between classes.
- </para>
-
- <para>
- AspectJ's <emphasis>aspect</emphasis> are the unit of modularity for
- crosscutting concerns. They behave somewhat like Java classes, but
- may also include pointcuts, advice and inter-type declarations.
- </para>
-
- <para>
- In the sections immediately following, we are first going to look at
- join points and how they compose into pointcuts. Then we will look at
- advice, the code which is run when a pointcut is reached. We will see
- how to combine pointcuts and advice into aspects, AspectJ's reusable,
- inheritable unit of modularity. Lastly, we will look at how to use
- inter-type declarations to deal with crosscutting concerns of a
- program's class structure.
- </para>
-
- <!-- ============================== -->
-
- <sect2 id="the-dynamic-join-point-model" xreflabel="the-dynamic-join-point-model">
- <title>The Dynamic Join Point Model</title>
-
- <para>
- A critical element in the design of any aspect-oriented language is
- the join point model. The join point model provides the common
- frame of reference that makes it possible to define the dynamic
- structure of crosscutting concerns. This chapter describes
- AspectJ's dynamic join points, in which join points are certain
- well-defined points in the execution of the program.
- </para>
-
- <para>
- AspectJ provides for many kinds of join points, but this chapter
- discusses only one of them: method call join points. A method call
- join point encompasses the actions of an object receiving a method
- call. It includes all the actions that comprise a method call,
- starting after all arguments are evaluated up to and including
- return (either normally or by throwing an exception).
- </para>
-
- <para>
- Each method call at runtime is a different join point, even if it
- comes from the same call expression in the program. Many other
- join points may run while a method call join point is executing --
- all the join points that happen while executing the method body,
- and in those methods called from the body. We say that these join
- points execute in the <emphasis>dynamic context</emphasis> of the
- original call join point.
- </para>
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="pointcuts-starting" xreflabel="pointcuts-starting">
- <title>Pointcuts</title>
-
- <para>
- In AspectJ, <emphasis>pointcuts</emphasis> pick out certain join
- points in the program flow. For example, the pointcut
- </para>
-
- <programlisting format="linespecific">
- call(void Point.setX(int))
- </programlisting>
-
- <para>
- picks out each join point that is a call to a method that has the
- signature <literal>void Point.setX(int)</literal> — that is,
- <classname>Point</classname>'s void <function>setX</function>
- method with a single <literal>int</literal> parameter.
- </para>
-
- <para>
- A pointcut can be built out of other pointcuts with and, or, and
- not (spelled <literal>&&</literal>, <literal>||</literal>,
- and <literal>!</literal>). For example:
- </para>
-
- <programlisting format="linespecific">
- call(void Point.setX(int)) ||
- call(void Point.setY(int))
- </programlisting>
-
- <para>
- picks out each join point that is either a call to
- <function>setX</function> or a call to <function>setY</function>.
- </para>
-
- <para>
- Pointcuts can identify join points from many different types
- — in other words, they can crosscut types. For example,
- </para>
-
- <programlisting format="linespecific">
- call(void FigureElement.setXY(int,int)) ||
- call(void Point.setX(int)) ||
- call(void Point.setY(int)) ||
- call(void Line.setP1(Point)) ||
- call(void Line.setP2(Point));
- </programlisting>
-
- <para>
- picks out each join point that is a call to one of five methods
- (the first of which is an interface method, by the way).
- </para>
-
- <para>
- In our example system, this pointcut captures all the join points
- when a <classname>FigureElement</classname> moves. While this is a
- useful way to specify this crosscutting concern, it is a bit of a
- mouthful. So AspectJ allows programmers to define their own named
- pointcuts with the <literal>pointcut</literal> form. So the
- following declares a new, named pointcut:
- </para>
-
- <programlisting format="linespecific">
- pointcut move():
- call(void FigureElement.setXY(int,int)) ||
- call(void Point.setX(int)) ||
- call(void Point.setY(int)) ||
- call(void Line.setP1(Point)) ||
- call(void Line.setP2(Point));
- </programlisting>
-
- <para>
- and whenever this definition is visible, the programmer can simply
- use <literal>move()</literal> to capture this complicated
- pointcut.
- </para>
-
- <para>
- The previous pointcuts are all based on explicit enumeration of a
- set of method signatures. We sometimes call this
- <emphasis>name-based</emphasis> crosscutting. AspectJ also
- provides mechanisms that enable specifying a pointcut in terms of
- properties of methods other than their exact name. We call this
- <emphasis>property-based</emphasis> crosscutting. The simplest of
- these involve using wildcards in certain fields of the method
- signature. For example, the pointcut
- </para>
-
- <programlisting format="linespecific">
- call(void Figure.make*(..))
- </programlisting>
-
- <para>
- picks out each join point that's a call to a void method defined
- on <classname>Figure</classname> whose the name begins with
- "<literal>make</literal>" regardless of the method's parameters.
- In our system, this picks out calls to the factory methods
- <function>makePoint</function> and <function>makeLine</function>.
- The pointcut
- </para>
-
- <programlisting format="linespecific">
- call(public * Figure.* (..))
- </programlisting>
-
- <para>
- picks out each call to <classname>Figure</classname>'s public
- methods.
- </para>
-
- <para>
- But wildcards aren't the only properties AspectJ supports.
- Another pointcut, <function>cflow</function>, identifies join
- points based on whether they occur in the dynamic context of
- other join points. So
- </para>
-
- <programlisting format="linespecific">
- cflow(move())
- </programlisting>
-
- <para>
- picks out each join point that occurs in the dynamic context of
- the join points picked out by <literal>move()</literal>, our named
- pointcut defined above. So this picks out each join points that
- occurrs between when a move method is called and when it returns
- (either normally or by throwing an exception).
- </para>
-
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="advice-starting" xreflabel="advice-starting">
- <title>Advice</title>
-
- <para>
- So pointcuts pick out join points. But they don't
- <emphasis>do</emphasis> anything apart from picking out join
- points. To actually implement crosscutting behavior, we use
- advice. Advice brings together a pointcut (to pick out join
- points) and a body of code (to run at each of those join points).
- </para>
-
- <para>
- AspectJ has several different kinds of advice. <emphasis>Before
- advice</emphasis> runs as a join point is reached, before the
- program proceeds with the join point. For example, before advice
- on a method call join point runs before the actual method starts
- running, just after the arguments to the method call are evaluated.
- </para>
-
- <programlisting><![CDATA[
- before(): move() {
- System.out.println("about to move");
- }
- ]]></programlisting>
-
- <para>
- <emphasis>After advice</emphasis> on a particular join point runs
- after the program proceeds with that join point. For example,
- after advice on a method call join point runs after the method body
- has run, just before before control is returned to the caller.
- Because Java programs can leave a join point 'normally' or by
- throwing an exception, there are three kinds of after advice:
- <literal>after returning</literal>, <literal>after
- throwing</literal>, and plain <literal>after</literal> (which runs
- after returning <emphasis>or</emphasis> throwing, like Java's
- <literal>finally</literal>).
- </para>
-
- <programlisting><![CDATA[
- after() returning: move() {
- System.out.println("just successfully moved");
- }
- ]]></programlisting>
-
- <para>
- <emphasis>Around advice</emphasis> on a join point runs as the join
- point is reached, and has explicit control over whether the program
- proceeds with the join point. Around advice is not discussed in
- this section.
- </para>
-
- <sect3>
- <title>Exposing Context in Pointcuts</title>
-
- <para>
- Pointcuts not only pick out join points, they can also expose
- part of the execution context at their join points. Values
- exposed by a pointcut can be used in the body of advice
- declarations.
- </para>
-
- <para>
- An advice declaration has a parameter list (like a method) that
- gives names to all the pieces of context that it uses. For
- example, the after advice
- </para>
-
- <programlisting><![CDATA[
- after(FigureElement fe, int x, int y) returning:
- ...SomePointcut... {
- ...SomeBody...
- }
- ]]></programlisting>
-
- <para>
- uses three pieces of exposed context, a
- <literal>FigureElement</literal> named fe, and two
- <literal>int</literal>s named x and y.
- </para>
-
- <para>
- The body of the advice uses the names just like method
- parameters, so
- </para>
-
- <programlisting><![CDATA[
- after(FigureElement fe, int x, int y) returning:
- ...SomePointcut... {
- System.out.println(fe + " moved to (" + x + ", " + y + ")");
- }
- ]]></programlisting>
-
- <para>
- The advice's pointcut publishes the values for the advice's
- arguments. The three primitive pointcuts
- <literal>this</literal>, <literal>target</literal> and
- <literal>args</literal> are used to publish these values. So now
- we can write the complete piece of advice:
- </para>
-
- <programlisting><![CDATA[
- after(FigureElement fe, int x, int y) returning:
- call(void FigureElement.setXY(int, int))
- && target(fe)
- && args(x, y) {
- System.out.println(fe + " moved to (" + x + ", " + y + ")");
- }
- ]]></programlisting>
-
- <para>
- The pointcut exposes three values from calls to
- <function>setXY</function>: the target
- <classname>FigureElement</classname> -- which it publishes as
- <literal>fe</literal>, so it becomes the first argument to the
- after advice -- and the two int arguments -- which it publishes
- as <literal>x</literal> and <literal>y</literal>, so they become
- the second and third argument to the after advice.
- </para>
-
- <para>
- So the advice prints the figure element
- that was moved and its new <literal>x</literal> and
- <literal>y</literal> coordinates after each
- <classname>setXY</classname> method call.
- </para>
-
- <para>
- A named pointcut may have parameters like a piece of advice.
- When the named pointcut is used (by advice, or in another named
- pointcut), it publishes its context by name just like the
- <literal>this</literal>, <literal>target</literal> and
- <literal>args</literal> pointcut. So another way to write the
- above advice is
- </para>
-
- <programlisting><![CDATA[
- pointcut setXY(FigureElement fe, int x, int y):
- call(void FigureElement.setXY(int, int))
- && target(fe)
- && args(x, y);
-
- after(FigureElement fe, int x, int y) returning: setXY(fe, x, y) {
- System.out.println(fe + " moved to (" + x + ", " + y + ").");
- }
- ]]></programlisting>
-
- </sect3>
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="inter-type-declarations" xreflabel="inter-type-declarations">
- <title>Inter-type declarations</title>
-
- <para>
- Inter-type declarations in AspectJ are declarations that cut across
- classes and their hierarchies. They may declare members that cut
- across multiple classes, or change the inheritance relationship
- between classes. Unlike advice, which operates primarily
- dynamically, introduction operates statically, at compile-time.
- </para>
-
- <para>
- Consider the problem of expressing a capability shared by some
- existing classes that are already part of a class hierarchy,
- i.e. they already extend a class. In Java, one creates an
- interface that captures this new capability, and then adds to
- <emphasis>each affected class</emphasis> a method that implements
- this interface.
- </para>
-
- <para>
- AspectJ can express the concern in one place, by using inter-type
- declarations. The aspect declares the methods and fields that are
- necessary to implement the new capability, and associates the
- methods and fields to the existing classes.
- </para>
-
- <para>
- Suppose we want to have <classname>Screen</classname> objects
- observe changes to <classname>Point</classname> objects, where
- <classname>Point</classname> is an existing class. We can implement
- this by writing an aspect declaring that the class Point
- <classname>Point</classname> has an instance field,
- <varname>observers</varname>, that keeps track of the
- <classname>Screen</classname> objects that are observing
- <classname>Point</classname>s.
- </para>
-
- <programlisting><![CDATA[
- aspect PointObserving {
- private Vector Point.observers = new Vector();
- ...
- }
- ]]></programlisting>
-
- <para>
- The <literal>observers</literal> field is private, so only
- <classname>PointObserving</classname> can see it. So observers are
- added or removed with the static methods
- <function>addObserver</function> and
- <function>removeObserver</function> on the aspect.
- </para>
-
- <programlisting><![CDATA[
- aspect PointObserving {
- private Vector Point.observers = new Vector();
-
- public static void addObserver(Point p, Screen s) {
- p.observers.add(s);
- }
- public static void removeObserver(Point p, Screen s) {
- p.observers.remove(s);
- }
- ...
- }
- ]]></programlisting>
-
- <para>
- Along with this, we can define a pointcut
- <function>changes</function> that defines what we want to observe,
- and the after advice defines what we want to do when we observe a
- change.
- </para>
-
- <programlisting><![CDATA[
- aspect PointObserving {
- private Vector Point.observers = new Vector();
-
- public static void addObserver(Point p, Screen s) {
- p.observers.add(s);
- }
- public static void removeObserver(Point p, Screen s) {
- p.observers.remove(s);
- }
-
- pointcut changes(Point p): target(p) && call(void Point.set*(int));
-
- after(Point p): changes(p) {
- Iterator iter = p.observers.iterator();
- while ( iter.hasNext() ) {
- updateObserver(p, (Screen)iter.next());
- }
- }
-
- static void updateObserver(Point p, Screen s) {
- s.display(p);
- }
- }
- ]]></programlisting>
-
- <para>
- Note that neither <classname>Screen</classname>'s nor
- <classname>Point</classname>'s code has to be modified, and that
- all the changes needed to support this new capability are local to
- this aspect.
- </para>
-
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="aspects" xreflabel="aspects">
- <title>Aspects</title>
-
- <para>
- Aspects wrap up pointcuts, advice, and inter-type declarations in a
- a modular unit of crosscutting implementation. It is defined very
- much like a class, and can have methods, fields, and initializers
- in addition to the crosscutting members. Because only aspects may
- include these crosscutting members, the declaration of these
- effects is localized.
- </para>
-
- <para>
- Like classes, aspects may be instantiated, but AspectJ controls how
- that instantiation happens -- so you can't use Java's
- <literal>new</literal> form to build new aspect instances. By
- default, each aspect is a singleton, so one aspect instance is
- created. This means that advice may use non-static fields of the
- aspect, if it needs to keep state around:
- </para>
-
- <programlisting><![CDATA[
- aspect Logging {
- OutputStream logStream = System.err;
-
- before(): move() {
- logStream.println("about to move");
- }
- }
- ]]></programlisting>
-
- <para>
- Aspects may also have more complicated rules for instantiation, but
- these will be described in a later chapter.
- </para>
-
- </sect2>
- </sect1>
-
- <!-- ============================== -->
-
- <sect1 id="starting-development" xreflabel="Development Aspects">
- <title>Development Aspects</title>
-
- <para>
- The next two sections present the use of aspects in increasingly
- sophisticated ways. Development aspects are easily removed from
- production builds. Production aspects are intended to be used in
- both development and in production, but tend to affect only a few
- classes.
- </para>
-
- <para>
- This section presents examples of aspects that can be used during
- development of Java applications. These aspects facilitate debugging,
- testing and performance tuning work. The aspects define behavior that
- ranges from simple tracing, to profiling, to testing of internal
- consistency within the application. Using AspectJ makes it possible
- to cleanly modularize this kind of functionality, thereby making it
- possible to easily enable and disable the functionality when desired.
- </para>
-
- <sect2 id="tracing" xreflabel="tracing">
- <title>Tracing</title>
-
- <para>
- This first example shows how to increase the visibility of the
- internal workings of a program. It is a simple tracing aspect that
- prints a message at specified method calls. In our figure editor
- example, one such aspect might simply trace whenever points are
- drawn.
- </para>
-
- <programlisting><![CDATA[
- aspect SimpleTracing {
- pointcut tracedCall():
- call(void FigureElement.draw(GraphicsContext));
-
- before(): tracedCall() {
- System.out.println("Entering: " + thisJoinPoint);
- }
- }
- ]]></programlisting>
-
- <para>
- This code makes use of the <literal>thisJoinPoint</literal> special
- variable. Within all advice bodies this variable is bound to an
- object that describes the current join point. The effect of this
- code is to print a line like the following every time a figure
- element receives a <function>draw</function> method call:
- </para>
-
- <programlisting><![CDATA[
- Entering: call(void FigureElement.draw(GraphicsContext))
- ]]></programlisting>
-
- <para>
- To understand the benefit of coding this with AspectJ consider
- changing the set of method calls that are traced. With AspectJ,
- this just requires editing the definition of the
- <function>tracedCalls</function> pointcut and recompiling. The
- individual methods that are traced do not need to be edited.
- </para>
-
- <para>
- When debugging, programmers often invest considerable effort in
- figuring out a good set of trace points to use when looking for a
- particular kind of problem. When debugging is complete or appears
- to be complete it is frustrating to have to lose that investment by
- deleting trace statements from the code. The alternative of just
- commenting them out makes the code look bad, and can cause trace
- statements for one kind of debugging to get confused with trace
- statements for another kind of debugging.
- </para>
-
- <para>
- With AspectJ it is easy to both preserve the work of designing a
- good set of trace points and disable the tracing when it isn t
- being used. This is done by writing an aspect specifically for that
- tracing mode, and removing that aspect from the compilation when it
- is not needed.
- </para>
-
- <para>
- This ability to concisely implement and reuse debugging
- configurations that have proven useful in the past is a direct
- result of AspectJ modularizing a crosscutting design element the
- set of methods that are appropriate to trace when looking for a
- given kind of information.
- </para>
- </sect2>
-
- <sect2 id="profiling-and-logging" xreflabel="profiling-and-logging">
- <title>Profiling and Logging</title>
-
- <para>
- Our second example shows you how to do some very specific
- profiling. Although many sophisticated profiling tools are
- available, and these can gather a variety of information and
- display the results in useful ways, you may sometimes want to
- profile or log some very specific behavior. In these cases, it is
- often possible to write a simple aspect similar to the ones above
- to do the job.
- </para>
-
- <para>
- For example, the following aspect counts the number of calls to the
- <function>rotate</function> method on a <classname>Line</classname>
- and the number of calls to the <function>set*</function> methods of
- a <classname>Point</classname> that happen within the control flow
- of those calls to <function>rotate</function>:
- </para>
-
- <programlisting><![CDATA[
- aspect SetsInRotateCounting {
- int rotateCount = 0;
- int setCount = 0;
-
- before(): call(void Line.rotate(double)) {
- rotateCount++;
- }
-
- before(): call(void Point.set*(int))
- && cflow(call(void Line.rotate(double))) {
- setCount++;
- }
- }
- ]]></programlisting>
-
- <para>
- In effect, this aspect allows the programmer to ask very specific
- questions like
-
- <blockquote>
- How many times is the <function>rotate</function>
- method defined on <classname>Line</classname> objects called?
- </blockquote>
-
- and
-
- <blockquote>
- How many times are methods defined on
- <classname>Point</classname> objects whose name begins with
- "<function>set</function>" called in fulfilling those rotate
- calls?
- </blockquote>
-
- questions it may be difficult to express using standard
- profiling or logging tools.
- </para>
-
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="pre-and-post-conditions" xreflabel="pre-and-post-conditions">
- <title>Pre- and Post-Conditions</title>
-
- <para>
- Many programmers use the "Design by Contract" style popularized by
- Bertand Meyer in <citetitle>Object-Oriented Software Construction,
- 2/e</citetitle>. In this style of programming, explicit
- pre-conditions test that callers of a method call it properly and
- explicit post-conditions test that methods properly do the work
- they are supposed to.
- </para>
-
- <para>
- AspectJ makes it possible to implement pre- and post-condition
- testing in modular form. For example, this code
- </para>
-
-
- <programlisting><![CDATA[
- aspect PointBoundsChecking {
-
- pointcut setX(int x):
- (call(void FigureElement.setXY(int, int)) && args(x, *))
- || (call(void Point.setX(int)) && args(x));
-
- pointcut setY(int y):
- (call(void FigureElement.setXY(int, int)) && args(*, y))
- || (call(void Point.setY(int)) && args(y));
-
- before(int x): setX(x) {
- if ( x < MIN_X || x > MAX_X )
- throw new IllegalArgumentException("x is out of bounds.");
- }
-
- before(int y): setY(y) {
- if ( y < MIN_Y || y > MAX_Y )
- throw new IllegalArgumentException("y is out of bounds.");
- }
- }
- ]]></programlisting>
-
- <para>
- implements the bounds checking aspect of pre-condition testing for
- operations that move points. Notice that the
- <function>setX</function> pointcut refers to all the operations
- that can set a Point's <literal>x</literal> coordinate; this
- includes the <function>setX</function> method, as well as half of
- the <function>setXY</function> method. In this sense the
- <function>setX</function> pointcut can be seen as involving very
- fine-grained crosscutting — it names the the
- <function>setX</function> method and half of the
- <function>setXY</function> method.
- </para>
-
- <para>
- Even though pre- and post-condition testing aspects can often be
- used only during testing, in some cases developers may wish to
- include them in the production build as well. Again, because
- AspectJ makes it possible to modularize these crosscutting concerns
- cleanly, it gives developers good control over this decision.
- </para>
-
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="contract-enforcement" xreflabel="contract-enforcement">
- <title>Contract Enforcement</title>
-
- <para>
- The property-based crosscutting mechanisms can be very useful in
- defining more sophisticated contract enforcement. One very powerful
- use of these mechanisms is to identify method calls that, in a
- correct program, should not exist. For example, the following
- aspect enforces the constraint that only the well-known factory
- methods can add an element to the registry of figure
- elements. Enforcing this constraint ensures that no figure element
- is added to the registry more than once.
- </para>
-
- <programlisting><![CDATA[
- aspect RegistrationProtection {
-
- pointcut register(): call(void Registry.register(FigureElement));
-
- pointcut canRegister(): withincode(static * FigureElement.make*(..));
-
- before(): register() && !canRegister() {
- throw new IllegalAccessException("Illegal call " + thisJoinPoint);
- }
- }
- ]]></programlisting>
-
- <para>
- This aspect uses the withincode primitive pointcut to denote all
- join points that occur within the body of the factory methods on
- <classname>FigureElement</classname> (the methods with names that
- begin with "<literal>make</literal>"). This is a property-based
- pointcut because it identifies join points based not on their
- signature, but rather on the property that they occur specifically
- within the code of another method. The before advice declaration
- effectively says signal an error for any calls to register that are
- not within the factory methods.
- </para>
-
- <para>
- This advice throws a runtime exception at certain join points, but
- AspectJ can do better. Using the <literal>declare error</literal>
- form, we can have the <emphasis>compiler</emphasis> signal the
- error.
- </para>
-
- <programlisting><![CDATA[
- aspect RegistrationProtection {
-
- pointcut register(): call(void Registry.register(FigureElement));
- pointcut canRegister(): withincode(static * FigureElement.make*(..));
-
- declare error: register() && !canRegister(): "Illegal call"
- }
- ]]></programlisting>
-
- <para>
- When using this aspect, it is impossible for the compiler to
- compile programs with these illegal calls. This early detection is
- not always possible. In this case, since we depend only on static
- information (the <literal>withincode</literal> pointcut picks out
- join points totally based on their code, and the
- <literal>call</literal> pointcut here picks out join points
- statically). Other enforcement, such as the precondition
- enforcement, above, does require dynamic information such as the
- runtime value of parameters.
- </para>
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="configuration-management" xreflabel="configuration-management">
- <title>Configuration Management</title>
-
- <para>
- Configuration management for aspects can be handled using a variety
- of make-file like techniques. To work with optional aspects, the
- programmer can simply define their make files to either include the
- aspect in the call to the AspectJ compiler or not, as desired.
- </para>
-
- <para>
- Developers who want to be certain that no aspects are included in
- the production build can do so by configuring their make files so
- that they use a traditional Java compiler for production builds. To
- make it easy to write such make files, the AspectJ compiler has a
- command-line interface that is consistent with ordinary Java
- compilers.
- </para>
- </sect2>
- </sect1>
-
- <!-- ============================== -->
-
- <sect1 id="starting-production" xreflabel="Production Aspects">
- <title>Production Aspects</title>
-
- <para>
- This section presents examples of aspects that are inherently
- intended to be included in the production builds of an application.
- Production aspects tend to add functionality to an application
- rather than merely adding more visibility of the internals of a
- program. Again, we begin with name-based aspects and follow with
- property-based aspects. Name-based production aspects tend to
- affect only a small number of methods. For this reason, they are a
- good next step for projects adopting AspectJ. But even though they
- tend to be small and simple, they can often have a significant
- effect in terms of making the program easier to understand and
- maintain.
- </para>
-
- <sect2 id="change-monitoring" xreflabel="change-monitoring">
- <title>Change Monitoring</title>
-
- <para>
- The first example production aspect shows how one might implement
- some simple functionality where it is problematic to try and do it
- explicitly. It supports the code that refreshes the display. The
- role of the aspect is to maintain a dirty bit indicating whether or
- not an object has moved since the last time the display was
- refreshed.
- </para>
-
- <para>
- Implementing this functionality as an aspect is straightforward.
- The <function>testAndClear</function> method is called by the
- display code to find out whether a figure element has moved
- recently. This method returns the current state of the dirty flag
- and resets it to false. The pointcut <function>move</function>
- captures all the method calls that can move a figure element. The
- after advice on <function>move</function> sets the dirty flag
- whenever an object moves.
- </para>
-
- <programlisting><![CDATA[
- aspect MoveTracking {
- private static boolean dirty = false;
-
- public static boolean testAndClear() {
- boolean result = dirty;
- dirty = false;
- return result;
- }
-
- pointcut move():
- call(void FigureElement.setXY(int, int)) ||
- call(void Line.setP1(Point)) ||
- call(void Line.setP2(Point)) ||
- call(void Point.setX(int)) ||
- call(void Point.setY(int));
-
- after() returning: move() {
- dirty = true;
- }
- }
- ]]></programlisting>
-
- <para>
- Even this simple example serves to illustrate some of the important
- benefits of using AspectJ in production code. Consider implementing
- this functionality with ordinary Java: there would likely be a
- helper class that contained the <literal>dirty</literal> flag, the
- <function>testAndClear</function> method, as well as a
- <function>setFlag</function> method. Each of the methods that could
- move a figure element would include a call to the
- <function>setFlag</function> method. Those calls, or rather the
- concept that those calls should happen at each move operation, are
- the crosscutting concern in this case.
- </para>
-
- <para>
- The AspectJ implementation has several advantages over the standard
- implementation:
- </para>
-
- <para>
- <emphasis>The structure of the crosscutting concern is captured
- explicitly.</emphasis> The moves pointcut clearly states all the
- methods involved, so the programmer reading the code sees not just
- individual calls to <literal>setFlag</literal>, but instead sees
- the real structure of the code. The IDE support included with
- AspectJ automatically reminds the programmer that this aspect
- advises each of the methods involved. The IDE support also
- provides commands to jump to the advice from the method and
- vice-versa.
- </para>
-
- <para>
- <emphasis>Evolution is easier.</emphasis> If, for example, the
- aspect needs to be revised to record not just that some figure
- element moved, but rather to record exactly which figure elements
- moved, the change would be entirely local to the aspect. The
- pointcut would be updated to expose the object being moved, and the
- advice would be updated to record that object. The paper
- <citetitle>An Overview of AspectJ</citetitle> (available linked off
- of the AspectJ web site -- <ulink
- url="http://eclipse.org/aspectj" />), presented at ECOOP
- 2001, presents a detailed discussion of various ways this aspect
- could be expected to evolve.
- </para>
-
- <para>
- <emphasis>The functionality is easy to plug in and out.</emphasis>
- Just as with development aspects, production aspects may need to be
- removed from the system, either because the functionality is no
- longer needed at all, or because it is not needed in certain
- configurations of a system. Because the functionality is
- modularized in a single aspect this is easy to do.
- </para>
-
- <para>
- <emphasis>The implementation is more stable.</emphasis> If, for
- example, the programmer adds a subclass of
- <classname>Line</classname> that overrides the existing methods,
- this advice in this aspect will still apply. In the ordinary Java
- implementation the programmer would have to remember to add the
- call to <function>setFlag</function> in the new overriding
- method. This benefit is often even more compelling for
- property-based aspects (see the section <xref
- linkend="starting-production-consistentBehavior"/>).
- </para>
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="context-passing" xreflabel="context-passing">
- <title>Context Passing</title>
-
- <para>
- The crosscutting structure of context passing can be a significant
- source of complexity in Java programs. Consider implementing
- functionality that would allow a client of the figure editor (a
- program client rather than a human) to set the color of any figure
- elements that are created. Typically this requires passing a color,
- or a color factory, from the client, down through the calls that
- lead to the figure element factory. All programmers are familiar
- with the inconvenience of adding a first argument to a number of
- methods just to pass this kind of context information.
- </para>
-
- <para>
- Using AspectJ, this kind of context passing can be implemented in a
- modular way. The following code adds after advice that runs only
- when the factory methods of <classname>Figure</classname> are
- called in the control flow of a method on a
- <classname>ColorControllingClient</classname>.
- </para>
-
- <programlisting><![CDATA[
- aspect ColorControl {
- pointcut CCClientCflow(ColorControllingClient client):
- cflow(call(* * (..)) && target(client));
-
- pointcut make(): call(FigureElement Figure.make*(..));
-
- after (ColorControllingClient c) returning (FigureElement fe):
- make() && CCClientCflow(c) {
- fe.setColor(c.colorFor(fe));
- }
- }
- ]]></programlisting>
-
- <para>
- This aspect affects only a small number of methods, but note that
- the non-AOP implementation of this functionality might require
- editing many more methods, specifically, all the methods in the
- control flow from the client to the factory. This is a benefit
- common to many property-based aspects while the aspect is short and
- affects only a modest number of benefits, the complexity the aspect
- saves is potentially much larger.
- </para>
-
- </sect2>
-
- <!-- ============================== -->
-
- <sect2 id="starting-production-consistentBehavior" xreflabel="Providing Consistent Behavior">
- <title>Providing Consistent Behavior</title>
-
- <para>
- This example shows how a property-based aspect can be used to
- provide consistent handling of functionality across a large set of
- operations. This aspect ensures that all public methods of the
- <literal>com.bigboxco</literal> package log any Errors they throw
- to their caller (in Java, an Error is like an Exception, but it
- indicates that something really bad and usually unrecoverable has
- happened). The <function>publicMethodCall</function> pointcut
- captures the public method calls of the package, and the after
- advice runs whenever one of those calls throws an Error. The advice
- logs that Error and then the throw resumes.
- </para>
-
- <programlisting><![CDATA[
- aspect PublicErrorLogging {
- Log log = new Log();
-
- pointcut publicMethodCall():
- call(public * com.bigboxco.*.*(..));
-
- after() throwing (Error e): publicMethodCall() {
- log.write(e);
- }
- }
- ]]></programlisting>
-
- <para>
- In some cases this aspect can log an exception twice. This happens
- if code inside the <literal>com.bigboxco</literal> package itself
- calls a public method of the package. In that case this code will
- log the error at both the outermost call into the
- <literal>com.bigboxco</literal> package and the re-entrant
- call. The <function>cflow</function> primitive pointcut can be used
- in a nice way to exclude these re-entrant calls:</para>
-
- <programlisting><![CDATA[
- after() throwing (Error e):
- publicMethodCall() && !cflow(publicMethodCall()) {
- log.write(e);
- }
- ]]></programlisting>
-
- <para>
- The following aspect is taken from work on the AspectJ compiler.
- The aspect advises about 35 methods in the
- <classname>JavaParser</classname> class. The individual methods
- handle each of the different kinds of elements that must be
- parsed. They have names like <function>parseMethodDec</function>,
- <function>parseThrows</function>, and
- <function>parseExpr</function>.
- </para>
-
- <programlisting><![CDATA[
- aspect ContextFilling {
- pointcut parse(JavaParser jp):
- call(* JavaParser.parse*(..))
- && target(jp)
- && !call(Stmt parseVarDec(boolean)); // var decs
- // are tricky
-
- around(JavaParser jp) returns ASTObject: parse(jp) {
- Token beginToken = jp.peekToken();
- ASTObject ret = proceed(jp);
- if (ret != null) jp.addContext(ret, beginToken);
- return ret;
- }
- }
- ]]></programlisting>
-
- <para>
- This example exhibits a property found in many aspects with large
- property-based pointcuts. In addition to a general property based
- pattern <literal>call(* JavaParser.parse*(..))</literal> it
- includes an exception to the pattern <literal>!call(Stmt
- parseVarDec(boolean))</literal>. The exclusion of
- <function>parseVarDec</function> happens because the parsing of
- variable declarations in Java is too complex to fit with the clean
- pattern of the other <function>parse*</function> methods. Even with
- the explicit exclusion this aspect is a clear expression of a clean
- crosscutting modularity. Namely that all
- <function>parse*</function> methods that return
- <classname>ASTObjects</classname>, except for
- <function>parseVarDec</function> share a common behavior for
- establishing the parse context of their result.
- </para>
-
- <para>
- The process of writing an aspect with a large property-based
- pointcut, and of developing the appropriate exceptions can clarify
- the structure of the system. This is especially true, as in this
- case, when refactoring existing code to use aspects. When we first
- looked at the code for this aspect, we were able to use the IDE
- support provided in AJDE for JBuilder to see what methods the
- aspect was advising compared to our manual coding. We quickly
- discovered that there were a dozen places where the aspect advice
- was in effect but we had not manually inserted the required
- functionality. Two of these were bugs in our prior non-AOP
- implementation of the parser. The other ten were needless
- performance optimizations. So, here, refactoring the code to
- express the crosscutting structure of the aspect explicitly made
- the code more concise and eliminated latent bugs.
- </para>
- </sect2>
- </sect1>
-
- <!-- ============================== -->
-
- <sect1 id="starting-conclusion">
- <title>Conclusion</title>
-
- <para>
- AspectJ is a simple and practical aspect-oriented extension to
- Java. With just a few new constructs, AspectJ provides support for
- modular implementation of a range of crosscutting concerns.
- </para>
-
- <para>
- Adoption of AspectJ into an existing Java development project can be
- a straightforward and incremental task. One path is to begin by using
- only development aspects, going on to using production aspects and
- then reusable aspects after building up experience with
- AspectJ. Adoption can follow other paths as well. For example, some
- developers will benefit from using production aspects right
- away. Others may be able to write clean reusable aspects almost right
- away.
- </para>
-
- <para>
- AspectJ enables both name-based and property based crosscutting.
- Aspects that use name-based crosscutting tend to affect a small
- number of other classes. But despite their small scale, they can
- often eliminate significant complexity compared to an ordinary Java
- implementation. Aspects that use property-based crosscutting can
- have small or large scale.
- </para>
-
- <para>
- Using AspectJ results in clean well-modularized implementations of
- crosscutting concerns. When written as an AspectJ aspect the
- structure of a crosscutting concern is explicit and easy to
- understand. Aspects are also highly modular, making it possible to
- develop plug-and-play implementations of crosscutting
- functionality.
- </para>
-
- <para>
- AspectJ provides more functionality than was covered by this short
- introduction. The next chapter, <xref linkend="language"/>,
- covers in detail more of the features of the AspectJ language. The
- following chapter, <xref linkend="examples"/>, then presents some
- carefully chosen examples that show you how AspectJ might be used. We
- recommend that you read the next two chapters carefully before
- deciding to adopt AspectJ into a project.
- </para>
- </sect1>
- </chapter>
-
|