diff options
author | acolyer <acolyer> | 2005-01-19 09:35:55 +0000 |
---|---|---|
committer | acolyer <acolyer> | 2005-01-19 09:35:55 +0000 |
commit | 0c8315087053985cae6defeaa2e3f9377968de88 (patch) | |
tree | 360906861b213337b67261f5978143a51e40343e /docs/adk15ProgGuideDB/ataspectj.xml | |
parent | feb085e611034c8217cbc060552826e6704ae07b (diff) | |
download | aspectj-0c8315087053985cae6defeaa2e3f9377968de88.tar.gz aspectj-0c8315087053985cae6defeaa2e3f9377968de88.zip |
updates for ltw and annotations
Diffstat (limited to 'docs/adk15ProgGuideDB/ataspectj.xml')
-rw-r--r-- | docs/adk15ProgGuideDB/ataspectj.xml | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/docs/adk15ProgGuideDB/ataspectj.xml b/docs/adk15ProgGuideDB/ataspectj.xml new file mode 100644 index 000000000..080199054 --- /dev/null +++ b/docs/adk15ProgGuideDB/ataspectj.xml @@ -0,0 +1,655 @@ +<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>Privileged aspects are declared as:</para> + + <programlisting><![CDATA[ + @Aspect(isPrivileged=true) + public class Foo {} + + is equivalent to... + + public privileged aspect Foo {} + ]]></programlisting> + + <para>To specify an aspect an aspect instantiation model (the default is + singleton), use the <literal>instantionModel</literal> and + <literal>perClausePattern</literal> attributes. For example:</para> + + <programlisting><![CDATA[ + @Aspect(instantiationModel=AspectInstantiationModel.PERTHIS, + perClausePattern="execution(* abc..*(..))") + public class Foo {} + + is equivalent to... + + public aspect Foo perthis(execution(* abc..*(..))) {} + ]]></programlisting> + + <para>The full definitions of the Aspect annotation type and the + AspectInstantiationModel enumerated type are:</para> + + <programlisting><![CDATA[ + /** + * Use to indicate that a class should be treated as an aspect by + * AspectJ's weaver. + */ + @Target({ElementType.TYPE}) + public @interface Aspect { + AspectInstantiationModel instantiationModel() default AspectInstantiationModel.SINGLETON; + String perClausePattern() default ""; + boolean isPrivileged() default false; + } + + /** + * The different aspect instantiation models supported by AspectJ + */ + public enum AspectInstantiationModel { + SINGLETON, + PERTHIS, + PERTARGET, + PERCFLOW, + PERCFLOWBELOW, + PERTYPEWITHIN + } + ]]></programlisting> + + </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> + <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. The method body should be empty and there should be no + throws clause. + </para> + + <para>A simple example:</para> + + <programlisting><![CDATA[ + @Pointcut("call(* *.*(..))") + void anyCall() {} + + is equivalent to... + + pointcut anyCall() : call(* *.*(..)); + ]]></programlisting> + + <para>An example with modifiers:</para> + + <programlisting><![CDATA[ + @Pointcut("") + protected abstract void anyCall(); + + is equivalent to... + + protected abstract pointcut anyCall(); + ]]></programlisting> + + <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 java.lang). This + to 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> + + <para>The <literal>value</literal> attribute of the + <literal>Pointcut</literal> declaration may contain any valid + AspectJ pointcut declaration.</para> + + </sect2> + + <sect2> + <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), and the restriction that advice cannot be + invoked explicitly (the weaver will issue an error if an advice method + is explicitly invoked).</para> + + <para>The following example shows a simple before advice declaration in + both styles:</para> + + <programlisting><![CDATA[ + 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>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> + was doing the calling, just add a parameter to the advice declaration.</para> + + <programlisting><![CDATA[ + @AdviceName("callFromFoo") + before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) { + System.out.println("Call from Foo: " + foo); + } + + is equivalent to... + + @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. In AspectJ 1.5.0 we require that these parameters be declared + first in the parameter list, in later releases we may relax this + requirement.</para> + + <programlisting><![CDATA[ + @AdviceName("callFromFoo") + before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) { + System.out.println("Call from Foo: " + foo + " at " + + thisJoinPoint); + } + + is equivalent to... + + @Before("call(* org.aspectprogrammer..*(..)) && this(foo)") + public void callFromFoo(JoinPoint thisJoinPoint, Foo 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[ + after() returning : criticalOperation() { + System.out.println("phew"); + } + + after() returning(Foo f) : call(Foo+.new(..)) { + System.out.println("It's a Foo: " + f); + } + + can be written as... + + @AfterReturning("criticalOperation()") + public void phew() { + System.out.println("phew"); + } + + @AfterReturning(value="call(Foo+.new(..))",returning="f") + public void itsAFoo(Foo f) { + System.out.println("It's a Foo: " + f); + } + ]]></programlisting> + + <para>(Note the need for the "value=" 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[ + public aspect ProceedAspect { + pointcut setAge(int i): call(* setAge(..)) && args(i); + + Object around(int i): setAge(i) { + return proceed(i*2); + } + } + + can be written as... + + @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(i*2); + } + + } + ]]></programlisting> + + </sect2> + + </sect1> + + <sect1 id="ataspectj-itds"> + <title>Inter-type Declarations</title> + + <para> + Inter-type declarations are challenging to support using an annotation style. + It's very important to preserve the exact same semantics between the code style + and the annotation style. We also want to support compilation of a large set + of AspectJ applications using a standard Java 5 compiler. For these reasons, in + the initial release of AspectJ 5 we will only support inter-type declarations + on interfaces using the annotation style. + </para> + + <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 { + + public interface Moody { + Mood getMood(); + }; + + @DeclareParents("org.xzy..*") + class MoodyImpl implements Moody { + private Mood mood = Mood.HAPPY; + + public Mood getMood() { + return mood; + } + } + + @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 interface implemented by the annotated class. In addition, the member + declarations within the annotated class are treated as inter-type declarations + on the implemented interface. 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>The annotated class may only extend <literal>Object</literal>, and may + only implement a single interface. The interface implemented by the class may + itself extend other interfaces. + </para> + + </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 will + 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 precedence and declare annotation <emphasis>will</emphasis> + be 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; + + // ... + } + + can be written as: + + @Aspect + @DeclarePrecedence("Security*,org.xyz.TransactionSupport,org.xyz.Persistence") + public class SystemArchitecture { + + // ... + } + ]]></programlisting> + + <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; + + } + + can be written as... + + @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>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> + + <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"; + + can be written as... + + @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"; + + ]]></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> + + <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> + |