1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144 |
- <chapter id="ataspectj" xreflabel="AtAspectJ">
-
- <title>An Annotation Based Development Style</title>
-
- <sect1 id="ataspectj-intro">
- <title>Introduction</title>
-
- <para>In addition to the familiar AspectJ code-based style of aspect
- declaration, AspectJ 5 also supports an annotation-based style of
- aspect declaration. We informally call the set of annotations that
- support this development style the "@AspectJ" annotations.</para>
-
- <para>
- AspectJ 5 allows aspects and their members to be specified using
- either the code style or the annotation style. Whichever style you
- use, the AspectJ weaver ensures that your program has exactly the
- same semantics. It is, to quote a famous advertising campaign,
- "a choice, not a compromise". The two styles can be mixed within
- a single application, and even within a single source file, though
- we doubt this latter mix will be recommended in practice.
- </para>
-
- <para>
- The use of the @AspectJ annotations means that there are large
- classes of AspectJ applications that can be compiled by a regular
- Java 5 compiler, and subsequently woven by the AspectJ weaver (for
- example, as an additional build stage, or as late as class load-time).
- In this chapter we introduce the @AspectJ annotations and show how
- they can be used to declare aspects and aspect members.
- </para>
-
- </sect1>
-
- <sect1 id="ataspectj-aspects">
- <title>Aspect Declarations</title>
-
- <para>
- Aspect declarations are supported by the
- <literal>org.aspectj.lang.annotation.Aspect</literal>
- annotation.
- The declaration:
- </para>
-
- <programlisting><![CDATA[
- @Aspect
- public class Foo {}
- ]]></programlisting>
-
- <para>Is equivalent to:</para>
-
- <programlisting><![CDATA[
- public aspect Foo {}
- ]]></programlisting>
-
- <para>To specify an aspect an aspect instantiation model (the default is
- singleton), provide the perclause as the
- <literal>@Aspect</literal>
- value.
- For example:
- </para>
-
- <programlisting><![CDATA[
- @Aspect("perthis(execution(* abc..*(..)))")
- public class Foo {}
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- public aspect Foo perthis(execution(* abc..*(..))) {}
- ]]></programlisting>
-
- <sect2 id="limitations" xreflabel="limitations">
- <title>Limitations</title>
-
- <para>Privileged aspects are not supported by the annotation style.</para>
- <!--
- <programlisting><![CDATA[
- @Aspect(isPrivileged=true)
- public class Foo {}
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- public privileged aspect Foo {}
- ]]></programlisting>
- -->
- </sect2>
-
- </sect1>
-
- <sect1 id="ataspectj-pcadvice">
- <title>Pointcuts and Advice</title>
-
- <para>
- Pointcut and advice declarations can be made using the
- <literal>Pointcut, Before, After, AfterReturning, AfterThrowing,</literal>
- and
- <literal>Around</literal>
- annotations.
- </para>
-
- <sect2 id="pointcuts" xreflabel="pointcuts">
- <title>Pointcuts</title>
-
- <para>
- Pointcuts are specified using the
- <literal>org.aspectj.lang.annotation.Pointcut</literal>
- annotation
- on a method declaration. The method should have a
- <literal>void</literal>
- return type. The parameters of the method correspond to the parameters
- of the pointcut. The modifiers of the method correspond to the modifiers
- of the pointcut.
- </para>
-
- <para>
- As a general rule, the
- <literal>@Pointcut</literal>
- annotated method must have an empty method body
- and must not have any
- <literal>throws</literal>
- clause. If formal are bound (using
- <literal>args(), target(), this(), @args(), @target(), @this(), @annotation())</literal>
- in the
- pointcut, then they must appear in the method signature.
- </para>
-
- <para>
- The
- <literal>if()</literal>
- pointcut is treated specially and is discussed in a later section.
- </para>
-
- <para>Here is a simple example of a pointcut declaration in both code and @AspectJ styles:</para>
-
- <programlisting><![CDATA[
- @Pointcut("call(* *.*(..))")
- void anyCall() {}
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- pointcut anyCall() : call(* *.*(..));
- ]]></programlisting>
-
-
- <para>When binding arguments, simply declare the arguments as normal in the annotated method:</para>
-
- <programlisting><![CDATA[
- @Pointcut("call(* *.*(int)) && args(i) && target(callee)")
- void anyCall(int i, Foo callee) {}
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- pointcut anyCall(int i, Foo callee) : call(* *.*(int)) && args(i) && target(callee);
- ]]></programlisting>
-
- <para>An example with modifiers (Remember that Java 5 annotations are not
- inherited, so the <literal>@Pointcut</literal> annotation must be
- present on the extending aspect's pointcut declaration too):</para>
-
- <programlisting><![CDATA[
- @Pointcut("")
- protected abstract void anyCall();
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- protected abstract pointcut anyCall();
- ]]></programlisting>
-
- <sect3>
- <title>Type references inside @AspectJ annotations</title>
-
- <para>
- Using the code style, types referenced in pointcut expressions are
- resolved with respect to the imported types in the compilation unit.
- When using the annotation style, types referenced in pointcut
- expressions are resolved in the absence of any imports and so have
- to be fully qualified if they are not by default visible to the
- declaring type (outside of the declaring package and
- <literal>java.lang</literal>
- ). This
- does not apply to type patterns with wildcards, which are always resolved
- in a global scope.
- </para>
-
- <para>
- Consider the following compilation unit:
- </para>
-
- <programlisting><![CDATA[
- package org.aspectprogrammer.examples;
-
- import java.util.List;
-
- public aspect Foo {
- pointcut listOperation() : call(* List.*(..));
- pointcut anyUtilityCall() : call(* java.util..*(..));
- }
- ]]></programlisting>
-
- <para>
- Using the annotation style this would be written as:
- </para>
-
- <programlisting><![CDATA[
- package org.aspectprogrammer.examples;
-
- import java.util.List; // redundant but harmless
-
- @Aspect
- public class Foo {
- @Pointcut("call(* java.util.List.*(..))") // must qualify
- void listOperation() {}
-
- @Pointcut("call(* java.util..*(..))")
- void anyUtilityCall() {}
- }
- ]]></programlisting>
-
- </sect3>
-
- <sect3>
- <title>if() pointcut expressions</title>
-
- <para>In code style, it is possible to use the
- <literal>if(...)</literal>
- poincut to define
- a conditional pointcut expression which will be evaluated at runtime for each candidate join point.
- The
- <literal>if(...)</literal>
- body can be any valid Java boolean expression, and can use any exposed formal, as well as the join
- point forms
- <literal>thisJoinPoint, thisJoinPointStaticPart and thisJoinPointEnclosingStaticPart</literal>
- .
- </para>
-
- <para>
- When using the annotation style, it is not possible to write a full Java expression
- within
- the annotation value so the syntax differs slightly, whilst providing the very same
- semantics and runtime behaviour. An
- <literal>if()</literal>
- pointcut expression can be
- declared in an
- <literal>@Pointcut</literal>
- , but must have either an empty body (<literal>if()</literal>, or be one
- of the expression forms
- <literal>if(true)</literal>
- or
- <literal>if(false)</literal>
- . The annotated
- method must be public, static, and return a boolean. The body of the method contains the
- condition to be evaluated. For example:
- </para>
-
- <programlisting><![CDATA[
- @Pointcut("call(* *.*(int)) && args(i) && if()")
- public static boolean someCallWithIfTest(int i) {
- return i > 0;
- }
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- pointcut someCallWithIfTest(int i) : call(* *.*(int)) && args(i) && if(i > 0);
- ]]></programlisting>
-
- <para>and the following is also a valid form:</para>
-
- <programlisting><![CDATA[
- static int COUNT = 0;
-
- @Pointcut("call(* *.*(int)) && args(i) && if()")
- public static boolean someCallWithIfTest(int i, JoinPoint jp, JoinPoint.EnclosingStaticPart esjp) {
- // any legal Java expression...
- return i > 0
- && jp.getSignature().getName.startsWith("doo")
- && esjp.getSignature().getName().startsWith("test")
- && COUNT++ < 10;
- }
-
- @Before("someCallWithIfTest(anInt, jp, enc)")
- public void beforeAdviceWithRuntimeTest(int anInt, JoinPoint jp, JoinPoint.EnclosingStaticPart enc) {
- //...
- }
-
- // Note that the following is NOT valid
- /*
- @Before("call(* *.*(int)) && args(i) && if()")
- public void advice(int i) {
- // so you were writing an advice or an if body ?
- }
- */
- ]]></programlisting>
-
- <para>
- It is thus possible with the annotation style to use the
- <literal>if()</literal>
- pointcut
- only within an
- <literal>@Pointcut</literal>
- expression. The
- <literal>if()</literal>
- must not contain any
- body. The annotated
- <literal>@Pointcut</literal>
- method must then be of the form
- <literal>public static boolean</literal>
- and can use formal bindings as usual.
- Extra
- <emphasis>implicit</emphasis>
- arguments of type JoinPoint, JoinPoint.StaticPart and JoinPoint.EnclosingStaticPart can also be used
- (this is not permitted for regular annotated pointcuts not using the
- <literal>if()</literal>
- form).
- </para>
-
- <para>
- The special forms
- <literal>if(true)</literal>
- and
- <literal>if(false)</literal>
- can be used in a more
- general way and don't imply that the pointcut method must have a body.
- You can thus write
- <literal>@Before("somePoincut() && if(false)")</literal>
- .
- </para>
-
- </sect3>
-
- </sect2>
-
- <sect2 id="advice" xreflabel="advice">
- <title>Advice</title>
-
- <para>In this section we first discuss the use of annotations for
- simple advice declarations. Then we show how
- <literal>thisJoinPoint</literal>
- and its siblings are handled in the body of advice and discuss the
- treatment of
- <literal>proceed</literal>
- in around advice.
- </para>
-
- <para>Using the annotation style, an advice declaration is written as
- a regular Java method with one of the
- <literal>Before, After, AfterReturning,
- AfterThrowing,</literal>
- or
- <literal>Around</literal>
- annotations. Except in
- the case of around advice, the method should return void. The method should
- be declared public.
- </para>
-
- <para>A method that has an advice annotation is treated exactly as an
- advice declaration by AspectJ's weaver. This includes the join points that
- arise when the advice is executed (an adviceexecution join point, not a
- method execution join point).</para>
-
- <para>The following example shows a simple before advice declaration in
- both styles:</para>
-
- <programlisting><![CDATA[
- @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
- public void callFromFoo() {
- System.out.println("Call from Foo");
- }
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- before() : call(* org.aspectprogrammer..*(..)) && this(Foo) {
- System.out.println("Call from Foo");
- }
- ]]></programlisting>
-
-
- <!--
- AMC: enhanced adviceexecution pointcuts and @AdviceName will most likely not make AJ5 1.5.0
-
- <para>Notice one slight difference between the two advice declarations: in
- the annotation style, the advice has a name, "callFromFoo". Even though
- advice cannot be invoked explicitly, this name is useful in join point
- matching when advising advice execution. For this reason, and to preserve
- exact semantic equivalence between the two styles, we also support the
- <literal>org.aspectj.lang.annotation.AdviceName</literal> annotation.
- The exact equivalent declarations are:
- </para>
-
- <programlisting><![CDATA[
- @AdviceName("callFromFoo")
- before() : call(* org.aspectprogrammer..*(..)) && this(Foo) {
- System.out.println("Call from Foo");
- }
-
- is equivalent to...
-
- @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
- public void callFromFoo() {
- System.out.println("Call from Foo");
- }
- ]]></programlisting>
- -->
-
- <para>If the advice body needs to know which particular
- <literal>Foo</literal>
- instance
- is making the call, just add a parameter to the advice declaration.
- </para>
-
- <programlisting><![CDATA[
- before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) {
- System.out.println("Call from Foo: " + foo);
- }
- ]]></programlisting>
-
- <para>can be written as:</para>
-
- <programlisting><![CDATA[
- @Before("call(* org.aspectprogrammer..*(..)) && this(foo)")
- public void callFromFoo(Foo foo) {
- System.out.println("Call from Foo: " + foo);
- }
- ]]></programlisting>
-
- <para>If the advice body needs access to
- <literal>thisJoinPoint</literal>
- ,
- <literal>thisJoinPointStaticPart</literal>
- ,
- <literal>thisEnclosingJoinPointStaticPart</literal>
- then these need to
- be declared as additional method parameters when using the annotation
- style.
- </para>
-
- <programlisting><![CDATA[
- @Before("call(* org.aspectprogrammer..*(..)) && this(foo)")
- public void callFromFoo(JoinPoint thisJoinPoint, Foo foo) {
- System.out.println("Call from Foo: " + foo + " at "
- + thisJoinPoint);
- }
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) {
- System.out.println("Call from Foo: " + foo + " at "
- + thisJoinPoint);
- }
- ]]></programlisting>
-
- <para>Advice that needs all three variables would be declared:</para>
-
- <programlisting><![CDATA[
- @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
- public void callFromFoo(JoinPoint thisJoinPoint,
- JoinPoint.StaticPart thisJoinPointStaticPart,
- JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart) {
- // ...
- }
- ]]></programlisting>
-
- <para>
- <literal>JoinPoint.EnclosingStaticPart</literal>
- is a new (empty) sub-interface
- of
- <literal>JoinPoint.StaticPart</literal>
- which allows the AspectJ weaver to
- distinguish based on type which of
- <literal>thisJoinPointStaticPart</literal>
- and
- <literal>thisEnclosingJoinPointStaticPart</literal>
- should be passed in a given
- parameter position.
- </para>
-
- <para>
- <literal>After</literal>
- advice declarations take exactly the same form
- as
- <literal>Before</literal>
- , as do the forms of
- <literal>AfterReturning</literal>
- and
- <literal>AfterThrowing</literal>
- that do not expose the return type or
- thrown exception respectively.
- </para>
-
- <para>
- To expose a return value with after returning advice simply declare the returning
- parameter as a parameter in the method body and bind it with the "returning"
- attribute:
- </para>
-
- <programlisting><![CDATA[
- @AfterReturning("criticalOperation()")
- public void phew() {
- System.out.println("phew");
- }
-
- @AfterReturning(pointcut="call(Foo+.new(..))",returning="f")
- public void itsAFoo(Foo f) {
- System.out.println("It's a Foo: " + f);
- }
- ]]></programlisting>
-
- <para>is equivalent to...</para>
-
- <programlisting><![CDATA[
- after() returning : criticalOperation() {
- System.out.println("phew");
- }
-
- after() returning(Foo f) : call(Foo+.new(..)) {
- System.out.println("It's a Foo: " + f);
- }
- ]]></programlisting>
-
- <para>(Note the use of the "pointcut=" prefix in front of the pointcut
- expression in the returning case).</para>
-
- <para>After throwing advice works in a similar fashion, using the
- <literal>throwing</literal>
- attribute when needing to expose a
- thrown exception.
- </para>
-
- <para>For around advice, we have to tackle the problem of
- <literal>proceed</literal>
- .
- One of the design goals for the annotation style is that a large class of
- AspectJ applications should be compilable with a standard Java 5 compiler.
- A straight call to
- <literal>proceed</literal>
- inside a method body:
- </para>
-
- <programlisting><![CDATA[
- @Around("call(* org.aspectprogrammer..*(..))")
- public Object doNothing() {
- return proceed(); // CE on this line
- }
- ]]></programlisting>
-
-
- <para>will result in a "No such method" compilation error. For this
- reason AspectJ 5 defines a new sub-interface of
- <literal>JoinPoint</literal>
- ,
- <literal>ProceedingJoinPoint</literal>
- .
- </para>
-
- <programlisting><![CDATA[
- public interface ProceedingJoinPoint extends JoinPoint {
- public Object proceed(Object[] args);
- }
- ]]></programlisting>
-
- <para>The around advice given above can now be written as:</para>
-
- <programlisting><![CDATA[
- @Around("call(* org.aspectprogrammer..*(..))")
- public Object doNothing(ProceedingJoinPoint thisJoinPoint) {
- return thisJoinPoint.proceed();
- }
- ]]></programlisting>
-
- <para>Here's an example that uses parameters for the proceed call:</para>
-
- <programlisting><![CDATA[
- @Aspect
- public class ProceedAspect {
-
- @Pointcut("call(* setAge(..)) && args(i)")
- void setAge(int i) {}
-
- @Around("setAge(i)")
- public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, int i) {
- return thisJoinPoint.proceed(new Object[]{i*2}); //using Java 5 autoboxing
- }
-
- }
- ]]></programlisting>
-
- <para>is equivalent to:</para>
-
- <programlisting><![CDATA[
- public aspect ProceedAspect {
- pointcut setAge(int i): call(* setAge(..)) && args(i);
-
- Object around(int i): setAge(i) {
- return proceed(i*2);
- }
- }
- ]]></programlisting>
-
- <para>Note that the ProceedingJoinPoint does not need to be passed to the proceed(..) arguments.
- </para>
- <para>In code style, the proceed method has the same signature as the advice, any reordering of
- actual arguments to the joinpoint that is done in the advice signature must be respected. Annotation
- style is different. The proceed(..) call takes, in this order:
- <itemizedlist>
- <listitem>If 'this()' was used in the pointcut <emphasis>for binding</emphasis>, it must be passed first in proceed(..).
- </listitem>
- <listitem>If 'target()' was used in the pointcut <emphasis>for binding</emphasis>, it must be passed next in proceed(..) - it will be the
- first argument to proceed(..) if this() was not used for binding.
- </listitem>
- <listitem>Finally come <emphasis>all</emphasis> the arguments expected at the join point, in the order they
- are supplied at the join point. Effectively the advice signature is ignored - it doesn't
- matter if a subset of arguments were bound or the ordering was changed in the advice
- signature, the proceed(..) calls takes all of them in the right order for the join point.
- </listitem>
- </itemizedlist>
- </para>
- <para>Since proceed(..) in this case takes an Object array, AspectJ cannot do as much compile time
- checking as it can for code style. If the rules above aren't obeyed then it will unfortunately
- manifest as a runtime error.
- </para>
- </sect2>
-
- </sect1>
-
- <sect1 id="ataspectj-itds">
- <title>Inter-type Declarations</title>
-
- <para>
- Inter-type declarations are challenging to support using an annotation style. For code style aspects
- compiled with the ajc compiler, the entire type system can be made aware of inter-type declarations (new
- supertypes, new methods, new fields) and the completeness and correctness of it can be guaranteed.
- Achieving this with an annotation style is hard because the source code may simply be compiled with javac
- where the type system cannot be influenced and what is compiled must be 'pure java'.
- </para>
- <para>
- AspectJ 1.5.0 introduced @DeclareParents, an attempt to offer something like that which is achievable with
- code style declare parents and the other intertype declarations (fields, methods, constructors). However,
- it has proved too challenging to get close to the expressiveness and capabilities of code style in this area
- and effectively @DeclareParents is offering just a mixin strategy. The definition of mixin I am using here is that when
- some interface I is mixed into some target type T then this means that all the methods from I are created in T and their
- implementations are simple forwarding methods that call a delegate which that provides an implementation of I.
- </para>
- <para>
- The next section covers @DeclareParents but AspectJ 1.6.4 introduces @DeclareMixin - an improved approach to defining
- a mixin and the choice of a different name for the annotation will hopefully alleviate some of the confusion about
- why @DeclareParents just doesn't offer the same semantics as the code style variant. Offering @DeclareMixin also gives
- code style developers a new tool for a simple mixin whereas previously they would have avoided @DeclareParents
- thinking what it could only do was already achievable with code style syntax.
- </para>
- <para>
- The defaultImpl attribute of @DeclareParents may become deprecated if @DeclareMixin proves popular, leaving
- @DeclareParents purely as a way to introduce a marker interface.
- </para>
-
-
- <sect2 id="atDeclareParents" xreflabel="atDeclareParents">
- <title>@DeclareParents</title>
-
- <para>
- Consider the following aspect:
- </para>
-
- <programlisting><![CDATA[
- public aspect MoodIndicator {
-
- public interface Moody {};
-
- private Mood Moody.mood = Mood.HAPPY;
-
- public Mood Moody.getMood() {
- return mood;
- }
-
- declare parents : org.xyz..* implements Moody;
-
- before(Moody m) : execution(* *.*(..)) && this(m) {
- System.out.println("I'm feeling " + m.getMood());
- }
- }
- ]]></programlisting>
-
- <para>
- This declares an interface
- <literal>Moody</literal>
- , and then makes two
- inter-type declarations on the interface - a field that is private to the
- aspect, and a method that returns the mood. Within the body of the inter-type
- declared method
- <literal>getMoody</literal>
- , the type of
- <literal>this</literal>
- is
- <literal>Moody</literal>
- (the target type of the inter-type declaration).
- </para>
-
- <para>Using the annotation style this aspect can be written:
- </para>
-
- <programlisting><![CDATA[
- @Aspect
- public class MoodIndicator {
-
- // this interface can be outside of the aspect
- public interface Moody {
- Mood getMood();
- };
-
- // this implementation can be outside of the aspect
- public static class MoodyImpl implements Moody {
- private Mood mood = Mood.HAPPY;
-
- public Mood getMood() {
- return mood;
- }
- }
-
- // the field type must be the introduced interface. It can't be a class.
- @DeclareParents(value="org.xzy..*",defaultImpl=MoodyImpl.class)
- private Moody implementedInterface;
-
- @Before("execution(* *.*(..)) && this(m)")
- void feelingMoody(Moody m) {
- System.out.println("I'm feeling " + m.getMood());
- }
- }
- ]]></programlisting>
-
- <para>
- This is very similar to the mixin mechanism supported by AspectWerkz. The
- effect of the
- <literal>@DeclareParents</literal>
- annotation is equivalent to
- a declare parents statement that all types matching the type pattern implement
- the given interface (in this case Moody).
- Each method declared in the interface is treated as an inter-type declaration.
- Note how this scheme operates within the constraints
- of Java type checking and ensures that
- <literal>this</literal>
- has access
- to the exact same set of members as in the code style example.
- </para>
-
- <para>
- Note that it is illegal to use the @DeclareParents annotation on an aspect' field of a non-interface type.
- The interface type is the inter-type declaration contract that dictates
- which methods are declared on the target type.
- </para>
-
- <programlisting><![CDATA[
- // this type will be affected by the inter-type declaration as the type pattern matches
- package org.xyz;
- public class MoodTest {
-
- public void test() {
- // see here the cast to the introduced interface (required)
- Mood mood = ((Moody)this).getMood();
- ...
- }
- }
- ]]></programlisting>
-
- <para>The <literal>@DeclareParents</literal> annotation can also be used without specifying
- a <literal>defaultImpl</literal> value (for example,
- <literal>@DeclareParents("org.xyz..*")</literal>). This is equivalent to a
- <literal>declare parents ... implements</literal> clause, and does <emphasis>not</emphasis>
- make any inter-type declarations for default implementation of the interface methods.
- </para>
-
- <para>
- Consider the following aspect:
- </para>
-
- <programlisting><![CDATA[
- public aspect SerializableMarker {
- declare parents : org.xyz..* implements Serializable;
- }
- ]]></programlisting>
-
- <para>Using the annotation style this aspect can be written:
- </para>
-
- <programlisting><![CDATA[
- @Aspect
- public class SerializableMarker {
- @DeclareParents("org.xyz..*")
- Serializable implementedInterface;
- }
- ]]></programlisting>
-
-
- <para>
- If the interface defines one or more operations, and these are not implemented by
- the target type, an error will be issued during weaving.
- </para>
-
- </sect2>
-
- <sect2 id="atDeclareMixin" xreflabel="atDeclareMixin">
- <title>@DeclareMixin</title>
- <para>
- Consider the following aspect:
- </para>
-
- <programlisting><![CDATA[
- public aspect MoodIndicator {
-
- public interface Moody {};
-
- private Mood Moody.mood = Mood.HAPPY;
-
- public Mood Moody.getMood() {
- return mood;
- }
-
- declare parents : org.xyz..* implements Moody;
-
- before(Moody m) : execution(* *.*(..)) && this(m) {
- System.out.println("I'm feeling " + m.getMood());
- }
- }
- ]]></programlisting>
-
- <para>
- This declares an interface <literal>Moody</literal>, and then makes two inter-type declarations on the interface
- - a field that is private to the aspect, and a method that returns the mood. Within the body of the inter-type
- declared method <literal>getMoody</literal>, the type of <literal>this</literal> is <literal>Moody</literal>
- (the target type of the inter-type declaration).
- </para>
-
- <para>Using the annotation style this aspect can be written:
- </para>
-
- <programlisting><![CDATA[
- @Aspect
- public class MoodIndicator {
-
- // this interface can be outside of the aspect
- public interface Moody {
- Mood getMood();
- };
-
- // this implementation can be outside of the aspect
- public static class MoodyImpl implements Moody {
- private Mood mood = Mood.HAPPY;
-
- public Mood getMood() {
- return mood;
- }
- }
-
- // The DeclareMixin annotation is attached to a factory method that can return instances of the delegate
- // which offers an implementation of the mixin interface. The interface that is mixed in is the
- // return type of the method.
- @DeclareMixin("org.xyz..*")
- public static Moody createMoodyImplementation() {
- return new MoodyImpl();
- }
-
- @Before("execution(* *.*(..)) && this(m)")
- void feelingMoody(Moody m) {
- System.out.println("I'm feeling " + m.getMood());
- }
- }
- ]]></programlisting>
-
- <para>
- Basically, the <literal>@DeclareMixin</literal> annotation is attached to a factory method. The
- factory method specifies the interface to mixin as its return type, and calling the method should
- create an instance of a delegate that implements the interface. This is the interface which will
- be delegated to from any target matching the specified type pattern.
- </para>
-
- <para>
- Exploiting this syntax requires the user to obey the rules of pure Java. So references to any
- targeted type as if it were affected by the Mixin must be made through a cast, like this:
- </para>
-
- <programlisting><![CDATA[
- // this type will be affected by the inter-type declaration as the type pattern matches
- package org.xyz;
- public class MoodTest {
-
- public void test() {
- // see here the cast to the introduced interface (required)
- Mood mood = ((Moody)this).getMood();
- ...
- }
- }
- ]]></programlisting>
-
- <para>
- Sometimes the delegate instance may want to perform differently depending upon the type/instance for
- which it is behaving as a delegate. To support this it is possible for the factory method to specify a
- parameter. If it does, then when the factory method is called the parameter will be the object instance for
- which a delegate should be created:
- </para>
- <programlisting><![CDATA[
- @Aspect
- public class Foo {
-
- @DeclareMixin("org.xyz..*")
- public static SomeInterface createDelegate(Object instance) {
- return new SomeImplementation(instance);
- }
- }
- ]]></programlisting>
-
- <para>
- It is also possible to make the factory method non-static - and in this case it can then exploit
- the local state in the surrounding aspect instance, but this is only supported for singleton aspects:
- </para>
- <programlisting><![CDATA[
- @Aspect
- public class Foo {
- public int maxLimit=35;
-
- @DeclareMixin("org.xyz..*")
- public SomeInterface createDelegate(Object instance) {
- return new SomeImplementation(instance,maxLimit);
- }
- }
- ]]></programlisting>
-
- <para>
- Although the interface type is usually determined purely from the return type of the factory method, it can
- be specified in the annotation if necessary. In this example the return type of the method extends multiple
- other interfaces and only a couple of them (I and J) should be mixed into any matching targets:
- </para>
- <programlisting><![CDATA[
- // interfaces is an array of interface classes that should be mixed in
- @DeclareMixin(value="org.xyz..*",interfaces={I.class,J.class})
- public static InterfaceExtendingLotsOfInterfaces createMoodyImplementation() {
- return new MoodyImpl();
- }
- ]]></programlisting>
-
- <para>
- There are clearly similarities between <literal>@DeclareMixin</literal> and <literal>@DeclareParents</literal> but
- <literal>@DeclareMixin</literal> is not pretending to offer more than a simple mixin strategy. The flexibility in
- being able to provide the factory method instead of requiring a no-arg constructor for the implementation also
- enables delegate instances to make decisions based upon the type for which they are the delegate.
- </para>
-
- <para>
- Any annotations defined on the interface methods are also put upon the delegate forwarding methods created in the
- matched target type.
- </para>
- </sect2>
-
- </sect1>
-
- <sect1 id="ataspectj-declare">
- <title>Declare statements</title>
-
- <para>The previous section on inter-type declarations covered the case
- of declare parents ... implements. The 1.5.0 release of AspectJ 5 does
- not support annotation style declarations for declare parents ... extends
- and declare soft (programs with these declarations would not in general
- be compilable by a regular Java 5 compiler, reducing the priority of
- their implementation). These may be supported in a future release.</para>
-
- <para>
- Declare annotation is also not supported in the 1.5.0 release of AspectJ 5.
- </para>
-
- <para>Declare precedence <emphasis>is</emphasis>
- supported. For declare precedence, use the
- <literal>@DeclarePrecedence</literal>
- annotation as in the following example:
- </para>
-
- <programlisting><![CDATA[
- public aspect SystemArchitecture {
- declare precedence : Security*, TransactionSupport, Persistence;
-
- // ...
- }
- ]]></programlisting>
-
- <para>
- can be written as:
- </para>
-
- <programlisting><![CDATA[
- @Aspect
- @DeclarePrecedence("Security*,org.xyz.TransactionSupport,org.xyz.Persistence")
- public class SystemArchitecture {
- // ...
- }
- ]]></programlisting>
-
- <!--
- note: below is not supported for now.
- <para>
- Declare annotation is supported via annotations on a dummy type member. If the
- <literal>Target</literal>
- specification of the annotation allows it, use a field,
- otherwise declare a member of the type required by the
- <literal>Target</literal>
- .
- For example:
- </para>
-
- <programlisting><![CDATA[
- public aspect DeclareAnnotationExamples {
- declare annotation : org.xyz.model..* : @BusinessDomain;
-
- declare annotation : public * BankAccount+.*(..) : @Secured(role="supervisor");
-
- declare anotation : * DAO+.* : @Persisted;
-
- }
- ]]></programlisting>
-
- <para>can be written as...</para>
-
- <programlisting><![CDATA[
- @Aspect
- public class DeclareAnnotationExamples {
-
- @DeclareAnnotation("org.xyz.model..*)
- @BusinessDomain Object modelClass;
-
- // this example assumes that the @Secured annotation has a Target
- // annotation with value ElementType.METHOD
- @DeclareAnnotation("public * org.xyz.banking.BankAccount+.*(..)")
- @Secured(role="supervisor) void bankAccountMethod();
-
- @DeclareAnnotation("* DAO+.*")
- @Persisted Object daoFields;
- }
- ]]></programlisting>
-
- <para>
- <emphasis>Note: Declare annotation is not available in AspectJ 1.5 M3 and syntax may change
- when the design and implementation is complete.</emphasis>
- </para>
- -->
- <para>We also support annotation style declarations for declare warning and
- declare error - any corresponding warnings and errors will be emitted at
- weave time, not when the aspects containing the declarations are compiled.
- (This is the same behaviour as when using declare warning or error with the
- code style). Declare warning and error declarations are made by annotating
- a string constant whose value is the message to be issued.</para>
-
- <para>Note that the String must be a literal and not the result of the invocation
- of a static method for example.</para>
-
- <programlisting><![CDATA[
- declare warning : call(* javax.sql..*(..)) && !within(org.xyz.daos..*)
- : "Only DAOs should be calling JDBC.";
-
- declare error : execution(* IFoo+.*(..)) && !within(org.foo..*)
- : "Only foo types can implement IFoo";
- ]]></programlisting>
-
- <para>can be written as...</para>
-
- <programlisting><![CDATA[
- @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
- static final String aMessage = "Only DAOs should be calling JDBC.";
-
- @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
- static final String badIFooImplementors = "Only foo types can implement IFoo";
-
- // the following is not valid since the message is not a String literal
- @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
- static final String badIFooImplementorsCorrupted = getMessage();
- static String getMessage() {
- return "Only foo types can implement IFoo " + System.currentTimeMillis();
- }
- ]]></programlisting>
-
- </sect1>
-
- <sect1 id="ataspectj-aspectof">
- <title>aspectOf() and hasAspect() methods</title>
-
- <para>A central part of AspectJ's programming model is that aspects
- written using the code style and compiled using ajc support
- <literal>aspectOf</literal>
- and
- <literal>hasAspect</literal>
- static
- methods. When developing an aspect using the annotation style and compiling
- using a regular Java 5 compiler, these methods will not be visible to the
- compiler and will result in a compilation error if another part of the
- program tries to call them.
- </para>
-
- <para>To provide equivalent support for AspectJ applications compiled with
- a standard Java 5 compiler, AspectJ 5 defines the
- <literal>Aspects</literal>
- utility class:
- </para>
-
- <programlisting><![CDATA[
- public class Aspects {
-
- /* variation used for singleton, percflow, percflowbelow */
- static<T> public static T aspectOf(T aspectType) {...}
-
- /* variation used for perthis, pertarget */
- static<T> public static T aspectOf(T aspectType, Object forObject) {...}
-
- /* variation used for pertypewithin */
- static<T> public static T aspectOf(T aspectType, Class forType) {...}
-
- /* variation used for singleton, percflow, percflowbelow */
- public static boolean hasAspect(Object anAspect) {...}
-
- /* variation used for perthis, pertarget */
- public static boolean hasAspect(Object anAspect, Object forObject) {...}
-
- /* variation used for pertypewithin */
- public static boolean hasAspect(Object anAspect, Class forType) {...}
- }
- ]]></programlisting>
-
- <!-- TODO AV - stuff below is not done -->
- <!--
- <para>When the AspectJ weaver sees calls to these methods, it will convert
- them into the most efficient form possible (to get performance equivalent
- to a direct <literal>MyAspect.aspectOf()</literal> call).</para>
- -->
- </sect1>
- </chapter>
-
|