Annotation definition

What is important to understand is that both the Annotation and the XML definition are just different views of the same underlying model. This means that they can easily co-exist and can succesfully be used together. The XML definition can be used as a replacement to the Annotation definition or as a complement. It can be used to refine and override definitions made in annotations as well as resolve missing pieces (for example pointcut definitions referenced but not defined) in the annotation definition. See the Choosing a definition model section for best practices.

AspectWerkz fully supports Java 5 annotations to define aspects or simply match on, and provides a Java 1.3/1.4 annotation API and custom compiler that allows to use the same feature with a very close user experience. Strongly typed Java 1.3/1.4 annotations facilities is thus provided.

Aspects

Even if all definition is made using annotations (and nothing in XML) a tiny deployment descriptor in XML still has to be written. This is needed for the runtime system to know which aspects it should load. See the XML deployment descriptor section for more information on how to write such a deployment descriptor.

When defining an aspect using annotations, the deployment model is specified as metadata argument of the metadata tag @Aspect.

@Aspect class level metadata has an optional anonymous parameter which specifies the deployment model. The default is perJVM if not specified.

  • @Aspect("perJVM") - deploys the aspect as perJVM. This is the default if only @Aspect is specified.


  • @Aspect("perClass") - deploys the aspect as perClass.


  • @Aspect("perInstance") - deploys the aspect as perInstance.


  • an optional name= named parameter which specify the name of the aspect. The default is the aspect class name if not specified.


It is also possible to not declare the @Aspect at all and define the deployment model in the XML descriptor.

Pointcut definition

In the aspect class you put the pointcut definitions as fields of the type org.codehaus.aspectwerkz.definition.Pointcut along with metadata annotations which specifies the type of (staticinitialization, execution, call, set, get, cflow or handler) and pattern for the pointcut.

If the pointcut is used to retain parameters value of the matching target (method/constructor execution/call and field value set) it has to be defined as a method, with a signature that conforms to the args() selector in the expression. For details see the Pointcuts section.

Advice definition

Define the type of the advice using annotation for the method along with the pointcut to which it the advice should be bound.

One of @Around, @Before, @After, @AfterFinally, @AfterReturning or @AfterThrowing annotations needs to be is specified. For details see the Advice section.

Source sample

The following source sample gives the main ideas of the annotation defined Aspect.

Java 5 annotations

@Aspect("perInstance")
public class MyAspect {

    @Introduce("within(com.mypackage.*)")
    MarkerInterface anIntroduction;

    @Expression("execution(* com.mypackage.Target.*(..))")
    Pointcut pc1;

    @Around("pc1")
    public Object advice1(final JoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some other stuff
        return result;
    }

     @Around("call(* com.mypackage.Target.*(..))")
    public Object advice2(final JoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some other stuff
        return result;
    }

    @Mixin("within(com.mypackage..*)")
    public static class MyIntroduction extends SuperMixin implements ContractualInterface {
        ... // introduced methods and fields
    }
}
                        

JavaDoc annotations

/**
 * @Aspect("perInstance")
 */
public class MyAspect {

    /**
     * @Introduce("within(com.mypackage.*)")
     */
    MarkerInterface anIntroduction;

    /**
     * @Expression("execution(* com.mypackage.Target.*(..))")
     */
    Pointcut pc1;

    /**
     * @Around("pc1")
     */
    public Object advice1(final JoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some other stuff
        return result;
    }

    /**
     * @Around("call(* com.mypackage.Target.*(..))")
     */
    public Object advice2(final JoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some other stuff
        return result;
    }

    /**
     * @Mixin("within(com.mypackage..*)")
     */
    public static class MyIntroduction extends SuperMixin implements ContractualInterface {
        ... // introduced methods and fields
    }
}
                        

Introduction definition

In the aspect class you specify the introduction to add as field for interface introduction (marker interface with no required method) or as public inner-class for mixins.

Interface introduction are added by adding a field to the aspect. The field type has to be the interface that you want to introduce. The @Introduce annotation is then used to specify the pattern matching the classes you want to apply the interface to. For details see the Introductions section.

Interface introduction with implementation, also known as mixins, are added by adding a public static inner-class to the aspect class. For details see the Introductions section.

Pointcuts

The Pointcut class implements the pointcut concept. A Pointcut picks out join points, i.e. selects well-defined points in the program flow.

This section describes how to define named pointcuts using annotations, for a detailed description on how pointcuts are written and composed see the Pointcut definition and Pointcut composition sections.

Named pointcuts are defined as fields in the Aspect class, or as method if they have a signature (that is using the args() selector), and they have to follow the following requirements:

  1. If defined as a field, the field is of type org.codehaus.aspectwerkz.definition.Pointcut and should be declared in the aspect class or hierarchy.


  2. The @Expression annotation allows us to define the type and pattern of the pointcut. There can be a composition, whether or not anonymous like in @Expression("anExpression OR call(<call pattern>)")


  3. If using the args() selector, the pointcut is defined as a method. The return type of the method does not matter, but the signature must conform to the args() selector (see samples below).


  4. The name of the pointcut must be unique - that is its signature does not allow to differenciate to pointcut defined as method. The methods names must differ.

The name of the pointcut is the field name if a field, or the method name if a method.

Java 5 annotations

    //-- defined as fields

    @Expression("execution(* com.package..*.*(..))")
    Poincut allMethods;

    @Expression("set(* com.package.Constants.*)")
    Pointcut allConstantAccess;

    //-- defined as methods

    @Expression("execution(* com.package..*.*(..)) && args(s)")
    Poincut allMethodsWithStringArg(String s) {return null;}

    @Expression("execution(* com.package..*.*(..)) && args(i, s, String ..)")
    void allMethodsWithIntArgTwoStringArgsAndSomeOtherArgs(int i, String s) {}
                    

JavaDoc annotations

    //-- defined as fields

    /**
     * @Expression("execution(* com.package..*.*(..))")
     */
    Poincut allMethods;

    /**
     * @Expression("set(* com.package.Constants.*)")
     */
    Pointcut allConstantAccess;

    //-- defined as methods

    /**
     * @Expression("execution(* com.package..*.*(..)) && args(s)")
     */
    Poincut allMethodsWithStringArg(String s) {return null;}

    /**
     * @Expression("execution(* com.package..*.*(..)) && args(i, s, String ..)")
     */
    void allMethodsWithIntArgTwoStringArgsAndSomeOtherArgs(int i, String s) {}
                    

Advice

The advice are implemented as regular methods in the Aspect class with the following requirements:

  1. The signature of the method is public Object <name of method>(JoinPoint joinPoint) throws Throwable (the public modifier is not mandatory, choose according to your needs) unless...
    • The advice is bounded to a pointcut with signature ( args()). In such a case, the parameter referenced in the pointcut signature must appear in the advice signature. The JoinPoint parameter must appear although not referenced by the pointcut signature. We can thus have any signature.


    • The method has a metadata that specifies the advice type and the matching joinpoints through a pointcut expression. The matching joinpoints are whether defined as an anonymous pointcut (and not a as a field) whether as an algebraic expression composed by other poincuts, whose type matches the advice type.

      If the expression is referencing a pointcut with a signature, then the parameters names as defined in the method signature must be passed to it.


The supported types of advice are the following.

  • @Around <poincut expression> - is invoked "around" the join point. Can be used to intercept method invocations on the 'callee' side.


  • @Before <poincut expression> - is invoked before the join point. Can be used for advising fields or method invocations on the 'caller' side.


  • @After <poincut expression> - is invoked after the join point. Can be used for advising fields or method invocations on the 'caller' side.


  • @AfterFinally <poincut expression> - same as @After (invoked after the join point).

    See details.


  • @AfterReturning <poincut expression> - is invoked after a method normal return (no exception thrown)

    See details.


  • @AfterThrowing <poincut expression> - is invoked after a method return with an exception.

    See details.

Both anonymous pointcut expressions and expressions made out of named pointcuts are supported. See example below.

Here is a simple example of an Around advice using Java 5 annotation style, the definition would be the same using JavaDoc annotations. (For more examples see the Examples section.)

Java 5 annotations

    //-- no arguments, JoinPoint is implictly mandatory

    /**
     * Non-anonymous expression (pc1 and pc2 are fields of type Pointcut with metadata).
     */
    @Around("pc1 && pc2")
    public Object myAroundAdvice1(StaticJoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some more stuff
        return result;
    }

    /**
     * Anonymous pointcuts.
     */
    @Around("execution(* com.package.Target.*(..)) || call(* com.package.Target.*(..))")
    public Object myAroundAdvice2(StaticJoinPoint joinPoint) throws Throwable {
        // do some stuff
        Object result = joinPoint.proceed();
        // do some more stuff
        return result;
    }

    //-- pointcut with signatures

    /**
     * Non-anonymous expression (pc1 is a method defining a Pointcut, with a String parameter).
     * Note the use of "s" in the metadata expression.
     */
    @Around("pc1(s)")
    public Object myAroundAdvice1WithArg(StaticJoinPoint joinPoint, String s) throws Throwable {
        // do some stuff
        // "s" can be accessed without the use of RTTI
        // Note that it can be modified according to the rules of Java Language references passing
        System.out.println(s);
        Object result = joinPoint.proceed();
        // do some more stuff
        return result;
    }

    /**
     * Anonymous pointcuts.
     */
    @Around("execution(* com.package.Target.*(..)) && args(arg0, int[][], arg2)")
    public Object myAroundAdvice2WithArg(StaticJoinPoint joinPoint, String arg0, com.Foo arg2) throws Throwable {
        // do some stuff
        // access arg0, arg2
        arg2.doSomething();
        Object result = joinPoint.proceed();
        // do some more stuff
        return result;
    }
                    

Introductions

AspectWerkz supports two types of introductions

  • Mixins - are separated from the aspect concept. They can be declared within an or outside of an aspect
  • Interface introductions - can only be declared within an aspect

Using annotations, introductions are defined as:

  • Fields of the aspect class whose type is the interface to introduce.

    Use interface introductions only if the interface does not require any implementation (marker interface).

    Fields are marked with the annotation @Introduce <pointcut>, where pointcut is composed with within, hasfield, hasmethod.


  • Public inner class of the aspect class.

    The inner class declares to implement the interface(s) to introduce and effectively implements it. The inner class is marked with the annotation @Mixin <pointcut>, where pointcut is composed with within, hasField, hasMethod.

Interface introductions

Interface introductions (introduction of marker interfaces) are defined as fields in the aspect class. The type of the field is the interface to introduce.

The field is marked with the annotation @Introduce <pointcut> to specify to which classes the introduction applies.

The introduction type is the field type.

When using abstract aspects or aspect inheritance, the aspect's super class can define interface introductions without specifying a @Introduce <pointcut> metadata. The concrete aspect should thus override the fields and specify the metadata defining to which classes the introduction applies.

Java 5 annotations

    @Introduce("within(com.package.*)")
    protected Serializable introduction1;

    @Introduce("within(com.package.*)")
    public OtherMarkerInterface introduction2;
                    

JavaDoc annotations

    /**
     * @Introduce("within(com.package.*)")
     */
    protected Serializable introduction1;

    /**
     * @Introduce("within(com.package.*)")
     */
    public OtherMarkerInterface introduction2;
                    

Mixins

For details on the mixin implementation and definition see this section.

Annotation reference

This section references all annotations used in the annotation definition.

Annotationsleveldefinesanonymous valueparameter(s)
[O:defaultValue] for optional parameter with default value
@AspectclassAspectperJVM | perClass | perInstance [O:perJVM]name=[0:<aspect class name>]
@ExpressionPointcut field, method with matching signaturePointcut, method execution<pointcut expression>
@IntroducefieldInterface Introduction<within/hasfield/hasmethod pointcut (and composition of those)>
@Mixinclass or inner classMixin<within/hasxxx pointcut>deploymentModel=(perJVM | perClass | perInstance [O:perInstance]), isTransient=(true | false [O:false])
@Aroundmethodaround advice<pointcut expression>
@Beforemethodbefore advice<pointcut expression>
@Aftermethodafter finally advice<pointcut expression>
@AfterFinallymethodafter finally advice<pointcut expression>
@AfterReturningmethodafter returning advice<pointcut expression>the FQN of the return type (optional)
@AfterThrowingmethodafter throwing advice<pointcut expression>the FQN of the exception type (optional)
@<name>anycustom annotation (typed or untyped)

Aspect annotation compilation

JavaDoc annotation defined aspects have to be post-compiled to incorporate the metadata into the class file bytecode. This post-compilation step will not be needed with Java 5 annotation defined aspects. For now you need to first compile the aspect classes with javac compiler and then post compile the .class files with the aspect source code as input.

To post-compile the aspect's .class files you have to use the AnnotationC compiler, which needs both the regular .class files and the aspect sources files containing the annotation metadata. You can run the AnnotationC from the command line. (It might be useful to run the ASPECTWERKZ_HOME/bin/setEnv.{bat|sh} script first.)

java [options...] org.codehaus.aspectwerkz.annotation.AnnotationC
    -src <path to src dirs>
    -srcpath <list of files>
    -srcincludes <path to file>
    -dest <path to destination dir>
    -custom <property file for cutom annotations>
    -verbose
            

Description of compiler options

Note: only one of -src, -srcpath and -srcincludes may be used.

  • -verbose - (optional) activates compilation status information


  • -src <path to src dirs> - provides the list of source directories separated by File.pathSeparator


  • -srcpath <list of files> - provides a comma separated list of source files


  • -srcincludes <path to file> - provides the path to a file containing the list of source files (one name per line)


  • -dest <path to destination dir> - (optional) if omitted the compiled classes will be written to the initial directory


  • -custom <property file for cutom annotations> - (optional) only needed if you have custom annotations you want to compile

AnnotationC can also be used to handle custom annotations as described here.

Note that if you are using the -dest option, the anonymous inner classes will not be copied to the destination directory, since the anonymous classes are not taken into account by the Annotation compiler. In such a case it is recommanded to add the following (if using Ant) just after the call to AnnotationC when the -dest option is used: (adapt according to the directories you are using)

<copy todir="classes/annotated" overwrite="false"> <fileset dir="classes/regular"/> </copy>

AnnotationC Ant task

We also have an Ant task for the AnnotationC compiler.

For details see this this section.

XML deployment descriptor

The deployment descriptor is needed for the runtime system to know which aspects it should load. It always has to be specified even though all definition is made in annotations.

Example:

<aspectwerkz>
    <system id="tests">
        <package name="foo.bar">
            <aspect class="MyAspect1"/>
            <aspect class="MyAspect2"/>
            ...
        </package>
    </system>
</aspectwerkz>