|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970 |
- [[starting]]
- = Getting Started with AspectJ
-
- [[starting-intro]]
- == Introduction
-
- 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:
-
- * Can I use aspects in my existing code?
- * What kinds of benefits can I expect to get from using aspects?
- * How do I find aspects in my programs?
- * How steep is the learning curve for AOP?
- * What are the risks of using this new technology?
-
- 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
- https://www.eclipse.org/aspectj/[AspectJ web site].
-
- 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:#starting-aspectj[Introduction to AspectJ], we present the core of
- AspectJ's features, and in xref:#starting-development[Development
- Aspects], we present aspects that facilitate tasks such as debugging,
- testing and performance tuning of applications. And, in the section
- following, xref:#starting-production[Production Aspects], we present
- aspects that implement crosscutting functionality common in Java
- applications. We will defer discussing a third category of aspects,
- reusable aspects, until xref:language.adoc#language[The AspectJ Language].
-
- 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.
-
- [[starting-aspectj]]
- == Introduction to AspectJ
-
- 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.
-
- The features are presented using a simple figure editor system. A
- `Figure` consists of a number of `FigureElements`, which can be either
- ``Point``s or ``Line``s. The `Figure` class provides factory services. There
- is also a `Display`. Most example programs later in this chapter are
- based on this system as well.
-
- image:images/figureUML.png[ UML for the `FigureEditor` example ]
-
- 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.
-
- 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.
-
- 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.
-
- 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.
-
- A _join point_ is a well-defined point in the program flow. A _pointcut_
- picks out certain join points and values at those points. A piece of
- _advice_ is code that is executed when a join point is reached. These
- are the dynamic parts of AspectJ.
-
- AspectJ also has different kinds of _inter-type declarations_ that allow
- the programmer to modify a program's static structure, namely, the
- members of its classes and the relationship between classes.
-
- AspectJ's _aspect_ are the unit of modularity for crosscutting concerns.
- They behave somewhat like Java classes, but may also include pointcuts,
- advice and inter-type declarations.
-
- 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.
-
- === The Dynamic Join Point Model
-
- 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.
-
- 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).
-
- 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 _dynamic
- context_ of the original call join point.
-
- [[pointcuts-starting]]
- === Pointcuts
-
- In AspectJ, _pointcuts_ pick out certain join points in the program
- flow. For example, the pointcut
-
- [source, java]
- ....
- call(void Point.setX(int))
- ....
-
- picks out each join point that is a call to a method that has the
- signature `void Point.setX(int)` - that is, ``Point``'s void `setX` method
- with a single `int` parameter.
-
- A pointcut can be built out of other pointcuts with and, or, and not
- (spelled `&&`, `||`, and `!`). For example:
-
- [source, java]
- ....
- call(void Point.setX(int)) ||
- call(void Point.setY(int))
- ....
-
- picks out each join point that is either a call to `setX` or a call to
- `setY`.
-
- Pointcuts can identify join points from many different types - in other
- words, they can crosscut types. For example,
-
- [source, java]
- ....
- 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));
- ....
-
- 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).
-
- In our example system, this pointcut captures all the join points when a
- `FigureElement` 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 `pointcut`
- form. So the following declares a new, named pointcut:
-
- [source, java]
- ....
- 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));
- ....
-
- and whenever this definition is visible, the programmer can simply use
- `move()` to capture this complicated pointcut.
-
- The previous pointcuts are all based on explicit enumeration of a set of
- method signatures. We sometimes call this _name-based_ crosscutting.
- AspectJ also provides mechanisms that enable specifying a pointcut in
- terms of properties of methods other than their exact name. We call this
- _property-based_ crosscutting. The simplest of these involve using
- wildcards in certain fields of the method signature. For example, the
- pointcut
-
- [source, java]
- ....
- call(void Figure.make*(..))
- ....
-
- picks out each join point that's a call to a void method defined on
- `Figure` whose the name begins with "`make`" regardless of the method's
- parameters. In our system, this picks out calls to the factory methods
- `makePoint` and `makeLine`. The pointcut
-
- [source, java]
- ....
- call(public * Figure.* (..))
- ....
-
- picks out each call to ``Figure``'s public methods.
-
- But wildcards aren't the only properties AspectJ supports. Another
- pointcut, `cflow`, identifies join points based on whether they occur in
- the dynamic context of other join points. So
-
- [source, java]
- ....
- cflow(move())
- ....
-
- picks out each join point that occurs in the dynamic context of the join
- points picked out by `move()`, 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).
-
- [[advice-starting]]
- === Advice
-
- So pointcuts pick out join points. But they don't _do_ 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).
-
- AspectJ has several different kinds of advice. _Before advice_ 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.
-
- [source, java]
- ....
- before(): move() {
- System.out.println("about to move");
- }
- ....
-
- _After advice_ 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: `after returning`, `after
- throwing`, and plain `after` (which runs after returning _or_
- throwing, like Java's `finally`).
-
- [source, java]
- ....
- after() returning: move() {
- System.out.println("just successfully moved");
- }
- ....
-
- _Around advice_ 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.
-
- ==== Exposing Context in Pointcuts
-
- 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.
-
- 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
-
- [source, java]
- ....
- after(FigureElement fe, int x, int y) returning:
- // SomePointcut...
- {
- // SomeBody
- }
- ....
-
- uses three pieces of exposed context, a `FigureElement` named fe, and
- two ``int``s named x and y.
-
- The body of the advice uses the names just like method parameters, so
-
- [source, java]
- ....
- after(FigureElement fe, int x, int y) returning:
- // SomePointcut...
- {
- System.out.println(fe + " moved to (" + x + ", " + y + ")");
- }
- ....
-
- The advice's pointcut publishes the values for the advice's arguments.
- The three primitive pointcuts `this`, `target` and `args` are used to
- publish these values. So now we can write the complete piece of advice:
-
- [source, java]
- ....
- 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 + ")");
- }
- ....
-
- The pointcut exposes three values from calls to `setXY`: the target
- `FigureElement` -- which it publishes as `fe`, so it becomes the first
- argument to the after advice -- and the two int arguments -- which it
- publishes as `x` and `y`, so they become the second and third argument
- to the after advice.
-
- So the advice prints the figure element that was moved and its new `x`
- and `y` coordinates after each `setXY` method call.
-
- 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 `this`, `target` and `args`
- pointcut. So another way to write the above advice is
-
- [source, java]
- ....
- 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 + ").");
- }
- ....
-
- === Inter-type declarations
-
- 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.
-
- 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 _each affected class_ a method that
- implements this interface.
-
- 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.
-
- Suppose we want to have `Screen` objects observe changes to `Point`
- objects, where `Point` is an existing class. We can implement this by
- writing an aspect declaring that the class Point `Point` has an instance
- field, `observers`, that keeps track of the `Screen` objects that are
- observing ``Point``s.
-
- [source, java]
- ....
- aspect PointObserving {
- private Vector Point.observers = new Vector();
- // ...
- }
- ....
-
- The `observers` field is private, so only `PointObserving` can see it.
- So observers are added or removed with the static methods `addObserver`
- and `removeObserver` on the aspect.
-
- [source, java]
- ....
- 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);
- }
- //...
- }
- ....
-
- Along with this, we can define a pointcut `changes` that defines what we
- want to observe, and the after advice defines what we want to do when we
- observe a change.
-
- [source, java]
- ....
- 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);
- }
- }
- ....
-
- Note that neither ``Screen``'s nor ``Point``'s code has to be modified, and
- that all the changes needed to support this new capability are local to
- this aspect.
-
- === Aspects
-
- 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.
-
- Like classes, aspects may be instantiated, but AspectJ controls how that
- instantiation happens -- so you can't use Java's `new` 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:
-
- [source, java]
- ....
- aspect Logging {
- OutputStream logStream = System.err;
-
- before(): move() {
- logStream.println("about to move");
- }
- }
- ....
-
- Aspects may also have more complicated rules for instantiation, but
- these will be described in a later chapter.
-
- [[starting-development]]
- == Development Aspects
-
- 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.
-
- 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.
-
- === Tracing
-
- 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.
-
- [source, java]
- ....
- aspect SimpleTracing {
- pointcut tracedCall():
- call(void FigureElement.draw(GraphicsContext));
-
- before(): tracedCall() {
- System.out.println("Entering: " + thisJoinPoint);
- }
- }
- ....
-
- This code makes use of the `thisJoinPoint` 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 `draw` method call:
-
- [source, text]
- ....
- Entering: call(void FigureElement.draw(GraphicsContext))
- ....
-
- 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 `tracedCalls` pointcut and
- recompiling. The individual methods that are traced do not need to be
- edited.
-
- 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.
-
- 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.
-
- 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.
-
- === Profiling and Logging
-
- 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.
-
- For example, the following aspect counts the number of calls to the
- `rotate` method on a `Line` and the number of calls to the `set*`
- methods of a `Point` that happen within the control flow of those calls
- to `rotate`:
-
- [source, java]
- ....
- 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++;
- }
- }
- ....
-
- In effect, this aspect allows the programmer to ask very specific
- questions like
-
- ____
- How many times is the `rotate` method defined on `Line` objects called?
- ____
-
- and
-
- ____
- How many times are methods defined on `Point` objects whose name begins with
- `"set"` called in fulfilling those `rotate` calls?
- ____
-
- Such questions may be difficult to express using standard profiling or
- logging tools.
-
- [[pre-and-post-conditions]]
- === Pre- and Post-Conditions
-
- Many programmers use the "Design by Contract" style popularized by
- Bertand Meyer in Object-Oriented Software Construction, 2/e. 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.
-
- AspectJ makes it possible to implement pre- and post-condition testing
- in modular form. For example, this code
-
- [source, java]
- ....
- 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.");
- }
- }
- ....
-
- implements the bounds checking aspect of pre-condition testing for
- operations that move points. Notice that the `setX` pointcut refers to
- all the operations that can set a Point's `x` coordinate; this includes
- the `setX` method, as well as half of the `setXY` method. In this sense
- the `setX` pointcut can be seen as involving very fine-grained
- crosscutting - it names the the `setX` method and half of the `setXY`
- method.
-
- 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.
-
- === Contract Enforcement
-
- 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.
-
- [source, java]
- ....
- aspect RegistrationProtection {
-
- pointcut register(): call(void Registry.register(FigureElement));
- pointcut canRegister(): withincode(static * FigureElement.make*(..));
-
- before(): register() && !canRegister() {
- throw new IllegalAccessException("Illegal call " + thisJoinPoint);
- }
- }
- ....
-
- This aspect uses the withincode primitive pointcut to denote all join
- points that occur within the body of the factory methods on
- `FigureElement` (the methods with names that begin with "`make`"). 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.
-
- This advice throws a runtime exception at certain join points, but
- AspectJ can do better. Using the `declare error` form, we can have the
- _compiler_ signal the error.
-
- [source, java]
- ....
- aspect RegistrationProtection {
-
- pointcut register(): call(void Registry.register(FigureElement));
- pointcut canRegister(): withincode(static * FigureElement.make*(..));
-
- declare error: register() && !canRegister(): "Illegal call"
- }
- ....
-
- 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
- `withincode` pointcut picks out join points totally based on their code,
- and the `call` 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.
-
- === Configuration Management
-
- 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.
-
- 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.
-
- [[starting-production]]
- == Production Aspects
-
- 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.
-
- === Change Monitoring
-
- 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.
-
- Implementing this functionality as an aspect is straightforward. The
- `testAndClear` 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 `move`
- captures all the method calls that can move a figure element. The after
- advice on `move` sets the dirty flag whenever an object moves.
-
- [source, java]
- ....
- 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;
- }
- }
- ....
-
- 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 `dirty` flag, the `testAndClear` method, as well as a
- `setFlag` method. Each of the methods that could move a figure element
- would include a call to the `setFlag` method. Those calls, or rather the
- concept that those calls should happen at each move operation, are the
- crosscutting concern in this case.
-
- The AspectJ implementation has several advantages over the standard
- implementation:
-
- _The structure of the crosscutting concern is captured explicitly._ The
- moves pointcut clearly states all the methods involved, so the
- programmer reading the code sees not just individual calls to `setFlag`,
- 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.
-
- _Evolution is easier._ 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
- An Overview of AspectJ (available linked off of the AspectJ web site --
- https://eclipse.org/aspectj[]), presented at ECOOP 2001, presents a
- detailed discussion of various ways this aspect could be expected to
- evolve.
-
- _The functionality is easy to plug in and out._ 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.
-
- _The implementation is more stable._ If, for example, the programmer
- adds a subclass of `Line` 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
- `setFlag` in the new overriding method. This benefit is often even more
- compelling for property-based aspects (see the section
- xref:#starting-production-consistentBehavior[Providing Consistent
- Behavior]).
-
- === Context Passing
-
- 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.
-
- 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 `Figure` are called in the control flow of a
- method on a `ColorControllingClient`.
-
- [source, java]
- ....
- 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));
- }
- }
- ....
-
- 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.
-
- [[starting-production-consistentBehavior]]
- === Providing Consistent Behavior
-
- 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 `com.bigboxco`
- 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 `publicMethodCall` 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.
-
- [source, java]
- ....
- aspect PublicErrorLogging {
- Log log = new Log();
-
- pointcut publicMethodCall():
- call(public * com.bigboxco.*.*(..));
-
- after() throwing (Error e): publicMethodCall() {
- log.write(e);
- }
- }
- ....
-
- In some cases this aspect can log an exception twice. This happens if
- code inside the `com.bigboxco` 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 `com.bigboxco` package and the re-entrant call.
- The `cflow` primitive pointcut can be used in a nice way to exclude
- these re-entrant calls:
-
- [source, java]
- ....
- after() throwing (Error e):
- publicMethodCall() && !cflow(publicMethodCall())
- {
- log.write(e);
- }
- ....
-
- The following aspect is taken from work on the AspectJ compiler. The
- aspect advises about 35 methods in the `JavaParser` class. The
- individual methods handle each of the different kinds of elements that
- must be parsed. They have names like `parseMethodDec`, `parseThrows`,
- and `parseExpr`.
-
- [source, java]
- ....
- 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;
- }
- }
- ....
-
- This example exhibits a property found in many aspects with large
- property-based pointcuts. In addition to a general property based
- pattern `call(* JavaParser.parse*(..))` it includes an exception to the
- pattern `!call(Stmt parseVarDec(boolean))`. The exclusion of `parseVarDec` happens
- because the parsing of variable declarations in Java is too complex to
- fit with the clean pattern of the other `parse*` methods. Even with the
- explicit exclusion this aspect is a clear expression of a clean
- crosscutting modularity. Namely that all `parse*` methods that return
- `ASTObjects`, except for `parseVarDec` share a common behavior for
- establishing the parse context of their result.
-
- 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.
-
- [[starting-conclusion]]
- == Conclusion
-
- 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.
-
- 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.
-
- 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.
-
- 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.
-
- AspectJ provides more functionality than was covered by this short
- introduction. The next chapter, xref:language.adoc#language[The AspectJ Language], covers in detail
- more of the features of the AspectJ language. The following chapter,
- xref:examples.adoc#examples[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.
|