Annotations Annotations in Java 5 This section provides the essential information about annotations in Java 5 needed to understand how annotations are treated in AspectJ 5. For a full introduction to annotations in Java, please see the documentation for the Java 5 SDK. Using Annotations Java 5 introduces annotation types which can be used to express metadata relating to program members in the form of annotations. Annotations in Java 5 can be applied to package and type declarations (classes, interfaces, enums, and annotations), constructors, methods, fields, parameters, and variables. Annotations are specified in the program source by using the @ symbol. For example, the following piece of code uses the @Deprecated annotation to indicate that the obsoleteMethod() has been deprecated: Annotations may be marker annotations, single-valued annotations, or multi-valued annotations. Annotation types with no members or that provide default values for all members may be used simply as marker annotations, as in the deprecation example above. Single-value annotation types have a single member, and the annotation may be written in one of two equivalent forms: or Multi-value annotations must use the member-name=value syntax to specify annotation values. For example: Retention Policies Annotations can have one of three retention policies: Source-file retention Annotations with source-file retention are read by the compiler during the compilation process, but are not rendered in the generated .class files. Class-file retention This is the default retention policy. Annotations with class-file retention are read by the compiler and also retained in the generated .class files. Runtime retention Annotations with runtime retention are read by the compiler, retained in the generated .class files, and also made available at runtime. Local variable annotations are not retained in class files (or at runtime) regardless of the retention policy set on the annotation type. See JLS 9.6.1.2. Accessing Annotations at Runtime Java 5 supports a new interface, java.lang.reflect.AnnotatedElement, that is implemented by the reflection classes in Java (Class, Constructor, Field, Method, and Package). This interface gives you access to annotations that have runtime retention via the getAnnotation, getAnnotations, and isAnnotationPresent. Because annotation types are just regular Java classes, the annotations returned by these methods can be queried just like any regular Java object. Annotation Inheritance It is important to understand the rules relating to inheritance of annotations, as these have a bearing on join point matching based on the presence or absence of annotations. By default annotations are not inherited. Given the following program Then Sub does not have the MyAnnotation annotation, and Sub.foo() is not an @Oneway method, despite the fact that it overrides Super.foo() which is. If an annotation type has the meta-annotation @Inherited then an annotation of that type on a class will cause the annotation to be inherited by sub-classes. So, in the example above, if the MyAnnotation type had the @Inherited attribute, then Sub would have the MyAnnotation annotation. @Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements. Annotating Aspects AspectJ 5 supports annotations on aspects, and on method, field, constructor, advice, and inter-type declarations within aspects. Method and advice parameters may also be annotated. Annotations are not permitted on pointcut declarations or on declare statements. The following example illustrates the use of annotations in aspects: An annotation on an aspect will be inherited by sub-aspects, iff it has the @Inherited meta-annotation. AspectJ 5 supports a new XLint warning, "the pointcut associated with this advice does not match any join points". The warning is enabled by default and will be emitted by the compiler if the pointcut expression associated with an advice statement can be statically determined to not match any join points. The warning can be suppressed for an individual advice statement by using the @SuppressWarnings({"unmatched"}) annotation. (See JLS 9.6.1.5). 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. For example: Matches any annotated element which has an annotation of type @Immutable. Matches any annotated element which does not have an annotation of type @Persistent. Matches any annotated element which has both an annotation of type @Foo and an annotation of type @Goo. (The parenthesis are required in this example). Matches any annotated element which has either an annotation of type @Foo or an annotation of type @Goo (or both). (The parenthesis are required in this example). Type Patterns In AspectJ 1.2, type patterns have the following form: AspectJ 1.5 extends type patterns to allow an optional AnnotationPattern prefix. The following examples illustrate the use of annotations in type patterns: Matches any type with the @Immutable annotation. Matches any type which does not have the @Immutable annotation. Matches any type in the org.xyz or org.abc packages with the @Immutable annotation. Matches a type Foo or any of its subtypes, which have the @Immutable annotation, or a type Goo. An AnnotationPattern has higher precedence than && or ||, so the previous expression is equivalent to ((@Immutable Foo+) || Goo). Matches any type in a package beginning with the prefix org.xyz, which has either the @Immutable annotation or the @NonPersistent annotation. Signature Patterns A FieldPattern is described by the following grammar: 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. A MethodPattern is of the form 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 The optional AnnotationPattern at the beginning of a method or constructor patterns 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 occuring 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, @call, @execution, @adviceexecution, @get, @set, @staticinitialization, @initialization,, and @preinitialization It is a compilation error to attempt to match on an annotation type that does not have runtime retention using one of these pointcut designators. 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: 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: 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: 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: 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 enclosing type (@within), or method/constructor (@withincode) has an annotation of the specified type. The form of these designators is: Some examples of using these designators follow: @within(@Foo) Matches any join point where the enclosing type has an annotation of type Foo. pointcut insideCriticalMethod(Critical c) : @withincode(c); Matches any join point whose shadow is lexically enclosed in a method which has an annotation of type @Critical, and exposes the value of the annotation in the parameter c. The remaining annotation-based pointcut designators are defined in a similar fashion: @call(@Foo) Matches any call join point where the most specific join point signature has an annotation of type @Foo. @execution(@Foo) Matches any execution join point where the most specific join point signature has an annotation of type @Foo. @adviceexecution(@Foo) Matches any advice execution join point where the advice has an annotation of type @Foo. @get(@Foo) Matches any get join point where the target field has an annotation of type @Foo. @set(@Foo) Matches any set join point where the target field has an annotation of type @Foo. @initialization(@Foo) Matches any initialization join point where the initiating constructor has an annotation of type @Foo. @preinitialization(@Foo) Matches any preinitialization join point where the initiating constructor has an annotation of type @Foo. @staticinitialization(@Foo) Matches any staticinitialization join point where the type being initialized has an annotation of type @Foo. 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. 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. 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: 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 AspectJ 5 allows you to annotate advice, but there is no way to qualify advice execution join point matching based on the presence of annotations. 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. Using Annotations with declare statements Declare error and declare warning Since pointcut expressions in AspectJ 5 support join point matching based on annotations, this facility can be exploited when writing declare warning and declare error statements. For example: declare parents The general form of a declare parents statement is: Since AspectJ 5 supports annotations as part of a type pattern specification, it is now possible to match types based on the presence of annotations with either class-file or runtime retention. For example: declare parents : (@Secured *) implements SecuredObject; All types with the @Secured annotation implement the SecuredObject inteface. declare parents : (@Secured BankAccount+) implements SecuredObject; The subset of types drawn from the BankAccount type and any subtype of BankAccount, where the @Secured annotation is present, implement the SecuredObject interface. An annotation type may not be used as the target of a declare parents statement. If an annotation type is named explicitly as the target of a declare parents statement, a compilation error will result. If an annotation type is matched by a non-explicit type pattern used in a declare parents statement it will be ignored (and an XLint warning issued). declare precedence The general form of a declare precedence statement is: AspectJ 5 allows the type patterns in the list to include annotation information as part of the pattern specification. For example: declare precedence : (@Security *),*; All aspects with the @Security annotation take precedence over any other aspects in the system. (Or, more informally, all security-related aspects take precedence). Declare Annotation AspectJ 5 supports a new kind of declare statement, declare annotation. The general form of a declare annotation statement is: Where annotation is a regular annotation expression as defined in the Java 5 language. If the annotation has the @Target meta-annotation, then the elements matched by ElementPattern must be of the kind specified by the @Target annotation. ElementPattern is defined as follows: The following examples illustrate the use of declare annotation. declare annotation : org.xyz.model..* : @BusinessDomain ; All types defined in a package with the prefix org.xyz.model have the @BusinessDomain annotation. declare annotation : public * BankAccount+.*(..) : @Secured(role="supervisor") All public methods in BankAccount and its subtypes have the annotation @Secured(role="supervisor"). declare annotation : * DAO+.* : @Persisted; All fields defined in DAO or its subtypes have the @Persisted annotation. Inter-type Declarations An annotation type may not be the target of an inter-type declaration.