diff options
-rw-r--r-- | docs/adk15ProgGuideDB/ataspectj.xml | 194 |
1 files changed, 185 insertions, 9 deletions
diff --git a/docs/adk15ProgGuideDB/ataspectj.xml b/docs/adk15ProgGuideDB/ataspectj.xml index 939a838c7..59d2e7996 100644 --- a/docs/adk15ProgGuideDB/ataspectj.xml +++ b/docs/adk15ProgGuideDB/ataspectj.xml @@ -648,17 +648,37 @@ <title>Inter-type Declarations</title> <para> - Inter-type declarations are challenging to support using an annotation style. - It's very important to preserve the 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, - the 1.5.0 release of AspectJ 5 only supports inter-type declarations - backed by interfaces when using the annotation style - - which means it is not possible to - introduce constructors or fields, as it would not be not possible to call - those unless already woven and available on a binary form. + 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 a mixin strategy. The definition of mixin I am using here is that + some interface I is mixed into some target type T and that means all the methods from I are added to T and their + implementations are simple forwarding methods that call a delegate which that provides an implementation of I. + </para> + <para> + The next section here talks about @DeclareParents and what is possible, but moving forward, starting with + AspectJ 1.6.4, we are offering @DeclareMixin - an improved approach to defining a mixin and the choice of a different + name 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 do was already achievable with + code style syntax. + </para> + <para> + In future releases the @DeclareParents support may be deprecated if it cannot be made more similar to code style. </para> + + <sect2 id="atDeclareParents" xreflabel="atDeclareParents"> + <title>@DeclareParents</title> + <para> Consider the following aspect: </para> @@ -798,6 +818,162 @@ 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> |