Join Point Matching based on Annotations
Prev | Chapter 2. Annotations | Next

## Join Point Matching based on Annotations

Note: compared to the previous version, this version restricts the use of annotations in type patterns (package annotations and outer type annotations cannot be specified inline), and requires parenthesis more often. These changes were made to make pointcut expressions easier to read and interpret.

This section discusses changes to type pattern and signature pattern matching in AspectJ 5 that support matching join points based on the presence or absence of annotations. We then discuss means of exposing annotation values within the body of advice.

### Annotation Patterns

For any kind of annotated element (type, method, constructor, package, etc.), an annotation pattern can be used to match against the set of annotations on the annotated element. Annotation patterns are defined by the following grammar.

    		AnnotationPattern := '!'? '@' AnnotationTypePattern AnnotationPattern*

    		AnnotationTypePattern := FullyQualifiedName |
    		                         '(' TypePattern ')'

      		FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*

In simple terms, an annotation pattern element has one of two basic forms:

- @<qualified-name>, for example, @Foo, or @org.xyz.Foo.
- @(<type-pattern>), for example, @(org.xzy..*), or @(Foo || Boo)

These simple elements may be negated using `!`, and combined by simple concatentation. The pattern `@Foo @Boo` matches an annotated element that has both an annotation of type `Foo` and an annotation of type `Boo`.

Some examples of annotation patterns follow:

          @Immutable

Matches any annotated element which has an annotation of type `Immutable`.

          !@Persistent

Matches any annotated element which does not have an annotation of type `Persistent`.

          @Foo @Goo

Matches any annotated element which has both an annotation of type `Foo` and an annotation of type `Goo`.

          @(Foo || Goo)

Matches any annotated element which has either an annotation of a type matching the type pattern `(Foo || Goo)`. In other words, an annotated element with either an annotation of type `Foo` or an annotation of type `Goo` (or both). (The parenthesis are required in this example).

          @(org.xyz..*)

Matches any annotated element which has either an annotation of a type matching the type pattern `(org.xyz..*)`. In other words, an annotated element with an annotation that is declared in the org.xyz package or a sub-package. (The parenthesis are required in this example).

### Type Patterns

AspectJ 1.5 extends type patterns to allow an optional `AnnotationPattern` prefix. (Extensions to this definition for generics are shown in the next chapter).

      	  	TypePattern := SimpleTypePattern |
      	  	               '!' TypePattern |
      	  	               '(' AnnotationPattern? TypePattern ')'
      	  	               TypePattern '&&' TypePattern |
      	  	               TypePattern '||' TypePattern |

      	  	SimpleTypePattern := DottedNamePattern '+'? '[]'*

      		DottedNamePattern := FullyQualifiedName RestOfNamePattern? |
      		                     '*' NotStarNamePattern?

      		RestOfNamePattern := '..' DottedNamePattern |
      		                     '*' NotStarNamePattern?

      		NotStarNamePattern := FullyQualifiedName RestOfNamePattern? |
      		                      '..' DottedNamePattern

      		FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*

Note that in most cases when annotations are used as part of a type pattern, the parenthesis are required (as in `(@Foo Hello+)`). In some cases (such as a type pattern used within a `this` pointcut expression, the parenthesis are optional:

            OptionalParensTypePattern := AnnotationPattern? TypePattern

The following examples illustrate the use of annotations in type patterns:

         (@Immutable *)

Matches any type with an `@Immutable` annotation.

         (!@Immutable *)

Matches any type which does not have an `@Immutable` annotation.

         (@Immutable (org.xyz.* || org.abc.*))

Matches any type in the `org.xyz` or `org.abc` packages with the `@Immutable` annotation.

         ((@Immutable Foo+) || Goo)

Matches a type `Foo` or any of its subtypes, which have the `@Immutable` annotation, or a type `Goo`.

         ((@(Immutable || NonPersistent) org.xyz..*)

Matches any type in a package beginning with the prefix `org.xyz`, which has either the `@Immutable` annotation or the `@NonPersistent` annotation.

         (@Immutable @NonPersistent org.xyz..*)

Matches any type in a package beginning with the prefix `org.xyz`, which has both an `@Immutable` annotation and an `@NonPersistent` annotation.

         (@(@Inherited *) org.xyz..*)

Matches any type in a package beginning with the prefix `org.xyz`, which has an inheritable annotation. The annotation pattern `@(@Inherited *)` matches any annotation of a type matching the type pattern `@Inherited *`, which in turn matches any type with the `@Inherited` annotation.

### Signature Patterns

A `FieldPattern` is described by the following grammar:

      		FieldPattern :=
      		    AnnotationPattern? FieldModifiersPattern?
      		    TypePattern (TypePattern DotOrDotDot)? SimpleNamePattern

    		FieldModifiersPattern := '!'? FieldModifier FieldModifiersPattern*

    		FieldModifier := 'public' | 'private' | 'protected' | 'static' |
    		                 'transient' | 'final'

    		DotOrDotDot := '.' | '..'

    		SimpleNamePattern := JavaIdentifierChar+ ('*' SimpleNamePattern)?

The optional `AnnotationPattern` restricts matches to fields with annotations that match the pattern. For example:

@SensitiveData * *

  Matches a field of any type and any name, that has an annotation of type `@SensitiveData`

@SensitiveData List org.xyz..*.*

  Matches a member field of a type in a package with prefix `org.xzy`, where the field is of type `List`, and has an annotation of type `@SensitiveData`

(@SensitiveData *) org.xyz..*.*

  Matches a member field of a type in a package with prefix `org.xzy`, where the field is of a type which has a `@SensitiveData` annotation.

@Foo (@Goo *) (@Hoo *).*

  Matches a field with an annotation `@Foo`, of a type with an annotation `@Goo`, declared in a type with annotation `@Hoo`.

@Persisted @Classified * *

  Matches a field with an annotation `@Persisted` and an annotation `@Classified`.

A `MethodPattern` is of the form

      		MethodPattern :=
      		    AnnotationPattern? MethodModifiersPattern? TypePattern
      		                       (TypePattern DotOrDotDot)? SimpleNamePattern
      		                       '(' FormalsPattern ')'ThrowsPattern?

    		MethodModifiersPattern := '!'? MethodModifier MethodModifiersPattern*

    		MethodModifier := 'public' | 'private' | 'protected' | 'static' |
    		                  'synchronized' | 'final'

    		FormalsPattern := '..' (',' FormalsPatternAfterDotDot)* |
    		                  OptionalParensTypePattern (',' FormalsPattern)* |
    		                  TypePattern '...'

    		FormalsPatternAfterDotDot :=
    		        OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
    		        TypePattern '...'

    		ThrowsPattern := 'throws' TypePatternList

    		TypePatternList := TypePattern (',' TypePattern)*

Note: compared to the previous version, this definition of MethodPattern does not allow parameter annotation matching (only matching on annotations of parameter types).

A `ConstructorPattern` has the form

      		ConstructorPattern :=
      		    AnnotationPattern? ConstructorModifiersPattern?
      		                       (TypePattern DotOrDotDot)? 'new' '(' FormalsPattern ')'
      		                       ThrowsPattern?

    		ConstructorModifiersPattern := '!'? ConstructorModifier ConstructorModifiersPattern*

    		ConstructorModifier := 'public' | 'private' | 'protected'

The optional `AnnotationPattern` at the beginning of a method or constructor pattern restricts matches to methods/constructors with annotations that match the pattern. For example:

@Oneway * *(..)

  Matches a method with any return type and any name, that has an annotation of type `@Oneway`.

@Transaction * (@Persistent org.xyz..*).*(..)

  Matches a method with the `@Transaction` annotation, declared in a type with the `@Persistent` annotation, and in a package beginning with the `org.xyz` prefix.

* *.*(@Immutable *,..)

  Matches any method taking at least one parameter, where the parameter type has an annotation `@Immutable`.

### Example Pointcuts

within(@Secure *)

  Matches any join point where the code executing is declared in a type with an `@Secure` annotation. The format of the `within` pointcut designator in AspectJ 5 is `'within' '(' OptionalParensTypePattern ')'`.

staticinitialization(@Persistent *)

  Matches the staticinitialization join point of any type with the `@Persistent` annotation. The format of the `staticinitialization` pointcut designator in AspectJ 5 is `'staticinitialization' '(' OptionalParensTypePattern ')'`.

call(@Oneway * *(..))

  Matches a call to a method with a `@Oneway` annotation.

execution(public (@Immutable *) org.xyz..*.*(..)

  The execution of any public method in a package with prefix `org.xyz`, where the method returns an immutable result.

set(@Cachable * *)

  Matches the set of any cachable field.

handler(!@Catastrophic *)

  Matches the handler join point for the handling of any exception that is not `Catastrophic`. The format of the `handler` pointcut designator in AspectJ 5 is `'handler' '(' OptionalParensTypePattern ')'`.

### Runtime type matching and context exposure

AspectJ 5 supports a set of "@" pointcut designators which can be used both to match based on the presence of an annotation at runtime, and to expose the annotation value as context in a pointcut or advice definition. These designators are `@args, @this, @target, @within, @withincode`, and `@annotation`

It is a compilation error to attempt to match on an annotation type that does not have runtime retention using `@this, @target` or `@args`. It is a compilation error to attempt to use any of these designators to expose an annotation value that does not have runtime retention.

The `this()`, `target()`, and `args()` pointcut designators allow matching based on the runtime type of an object, as opposed to the statically declared type. In AspectJ 5, these designators are supplemented with three new designators : `@this()` (read, "this annotation"), `@target()`, and `@args()`.

Like their counterparts, these pointcut designators can be used both for join point matching, and to expose context. The format of these new designators is:

      	    AtThis := '@this' '(' AnnotationOrIdentifer ')'

      	    AtTarget := '@target' '(' AnnotationOrIdentifier ')'

      	    AnnotationOrIdentifier := '@' FullyQualifiedName | Identifier

      	    AtArgs := '@args' '(' AnnotationsOrIdentifiersPattern ')'

      	    AnnotationsOrIdentifiersPattern :=
      	                      '..' (',' AnnotationsOrIdentifiersPatternAfterDotDot)? |
      	                      AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPattern)* |
      	                      '*' (',' AnnotationsOrIdentifiersPattern)*

      	    AnnotationsOrIdentifiersPatternAfterDotDot :=
    		                  AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPatternAfterDotDot)* |
    		                  '*' (',' AnnotationsOrIdentifiersPatternAfterDotDot)*

The forms of `@this()` and `@target()` that take a single annotation name are analogous to their counterparts that take a single type name. They match at join points where the object bound to `this` (or `target`, respectively) has an annotation of the specified type. For example:

@this(@Foo)

  Matches any join point where the object currently bound to 'this' has an annotation of type `Foo`.

call(* *(..)) && @target(@Classified)

  Matches a call to any object where the target of the call has a `@Classified` annotation.

Annotations can be exposed as context in the body of advice by using the forms of `@this(), @target()` and `@args()` that use bound variables in the place of annotation names. For example:

      	pointcut callToClassifiedObject(Classified classificationInfo) :
      	    call(* *(..)) && @target(classificationInfo);

      	pointcut txRequiredMethod(Tx transactionAnnotation) :
      	    execution(* *(..)) && @this(transactionAnnotation)
      	    && if(transactionAnnotation.policy == Tx.Policy.REQUIRED);

The `@args` pointcut designator behaves as its `args` counterpart, matching join points based on number and position of arguments, and supporting the `*` wildcard and at most one `..` wildcard. An annotation at a given position in an `@args` expression indicates that the runtime type of the argument in that position at a join point must have an annotation of the indicated type. For example:

      	/**
      	 * matches any join point with at least one argument, and where the
      	 * type of the first argument has the @Classified annotation
      	 */
      	pointcut classifiedArgument() : @args(@Classified,..);

      	/**
      	 * matches any join point with three arguments, where the third
      	 * argument has an annotation of type @Untrusted.
      	 */
      	pointcut untrustedData(Untrusted untrustedDataSource) :
      	    @args(*,*,untrustedDataSource);

Note: an alternative design would be to allow both annotation patterns and type patterns to be specified in the existing args pcd. This works well for matching, but is more awkward when it comes to exposing context.

Access to `AnnotatedElement` information is available reflectively with the body of advice through the `thisJoinPoint`, `thisJoinPointStaticPart`, and `thisEnclosingJoinPointStaticPart` variables. To access annotations on the arguments, or object bound to this or target at a join point you can use the following code fragments:

      	Annotation[] thisAnnotations = thisJoinPoint.getThis().getClass().getAnnotations();
      	Annotation[] targetAnnotations = thisJoinPoint.getTarget().getClass().getAnnotations();
      	Annotation[] firstParamAnnotations = thisJoinPoint.getArgs()[0].getClass().getAnnotations();

Note: it would be nicer to provide direct helper methods in the JoinPoint interface or a sub-interface that provide the annotations directly, something like "AnnotatedElement getThisAnnotationInfo()". The problem here is that the "AnnotatedElement" type is only in the Java 5 runtime libraries, and we don't want to tie the AspectJ runtime library to Java 5. A sub-interface and downcast solution could be used if these helpers were felt to be sufficiently important.

The `@within` and `@withincode` pointcut designators match any join point where the executing code is defined within a type (`@within`), or a method/constructor (`@withincode`) that has an annotation of the specified type. The form of these designators is:

            AtWithin := '@within' '(' AnnotationOrIdentifier ')'
            AtWithinCode := '@withincode' '(' AnnotationOrIdentifier ')'

Some examples of using these designators follow:

@within(@Foo)

  Matches any join point where the executing code is defined within a type which has an annotation of type `Foo`.

pointcut insideCriticalMethod(Critical c) : @withincode(c);

  Matches any join point where the executing code is defined in a method or constructor which has an annotation of type `@Critical`, and exposes the value of the annotation in the parameter `c`.

The `@annotation` pointcut designator matches any join point where the subject of the join point has an annotation of the given type. Like the other @pcds, it can also be used for context exposure.

            AtAnnotation := '@annotation' '(' AnnotationOrIdentifier ')'

The subject of a join point is defined in the table in chapter one of this guide.

Access to annotation information on members at a matched join point is also available through the `getSignature` method of the `JoinPoint` and `JoinPoint.StaticPart` interfaces. The `MemberSignature` interface is extended with the additional operation `java.lang.reflect.AccessibleObject getAccessibleObject()`. The following fragment illustrates an example use of this interface to access annotation information.

      	Signature sig = thisJoinPointStaticPart.getSignature();
      	AnnotatedElement declaringTypeAnnotationInfo = sig.getDeclaringType();
      	if (sig instanceof MemberSignature) {
      	  // this must be an initialization, pre-initialization, call, execution, get, or
      	  // set join point.
      	  AnnotatedElement memberAnnotationInfo = ((MemberSignature)sig).getAccessibleObject();
      	}

Note again that it would be nicer to add the method getAnnotationInfo directly to MemberSignature, but this would once more couple the runtime library to Java 5.

The `@this,@target` and `@args` pointcut designators can only be used to match against annotations that have runtime retention. The `@within, @withincode` and `@annotation` pointcut designators can only be used to match against annotations that have at least class-file retention, and if used in the binding form the annotation must have runtime retention.

### Package and Parameter Annotations

Note: A previous design allowed package annotation patterns to be specified directly in type patterns, and parameter annotation patterns to be specified directly in method and constructor signature patterns. Because this made some pointcut expressions hard to read and understand, we moved in favour of the design presented below, which also has its drawbacks. Matching on package and parameter annotations will be deferred until after the 1.5.0 release so that we can gain more understanding of the kinds of uses AspectJ users are making of annotations in pointcut expressions before commiting to any one approach.

### Annotation Inheritance and pointcut matching

According to the Java 5 specification, non-type annotations are not inherited, and annotations on types are only inherited if they have the `@Inherited` meta-annotation. Given the following program:

      	class C1 {
      	  @SomeAnnotation
      	  public void aMethod() {...}
      	}

      	class C2 extends C1 {
      	  public void aMethod() {...}
      	}

      	class Main {
      	  public static void main(String[] args) {
      	    C1 c1 = new C1();
      	    C2 c2 = new C2();
      	    c1.aMethod();
      	    c2.aMethod();
      	  }
      	}

      	aspect X {

      	  pointcut annotatedMethodCall() :
      	    call(@SomeAnnotation * C1.aMethod());

      	  pointcut c1MethodCall() :
      	    call(* C1.aMethod());
      	}

The pointcut `annotatedMethodCall` will match the call to `c1.aMethod()`, but not the call to `c2.aMethod()`.

The pointcut `c1MethodCall` matches both `c1.aMethod()` and `c2.aMethod()`.

### Limitations

It would be useful to be able to match join points based on annotation values, rather than merely the presence of a class-file retention annotation of a given type. This facility may be supported in a future version of AspectJ, by expanding the definition of `AnnotationPattern`. Matching annotation values for annotations with runtime retention can be done by exposing the annotation value as a pointcut parameter and then using an `if` pointcut expression to test the value.

Prev | Home | Next
Annotating Aspects | Up | Using Annotations with declare statements