JDK 5.0 has introduced a new concept called annotations. Annotations can be used as an alternative to XML for configuring classes for AOP. For backward compatibility with JDK 1.4.2 JBoss AOP uses an annotation compiler allowing you to create the same annotations in javadoc style comments.
The JDK 1.5 form has been used for the declarations of each annotation type shown below. For clarity both types of annotations are shown in the usage examples contained in this chapter. A point worth mentioning is that in JDK 1.5 annotations are part of language and can thus be imported, so that just the classname can be used. In JDK 1.4.2 the annotations are not part of "Java" so fully qualified classnames are needed. To keep things short and sweet the listings only import classes that are new for each listing.
To mark a class as an aspect you annotate it with the @Aspect annotation. Remember that a class to be used as an aspect does not need to inherit or implement anything special, but it must have an empty constuctor and contain one or more methods (advices) of the format:
public Object <any-method-name>(org.jboss.aop.joinpoint.Invocation)
The declaration of org.jboss.aop.Aspect is:
package org.jboss.aop; import org.jboss.aop.advice.Scope; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { Scope scope() default Scope.PER_VM; }
and Scope is:
package org.jboss.aop.advice; public enum Scope { PER_VM, PER_CLASS, PER_INSTANCE, PER_JOINPOINT }
See the "XML Bindings" chapter for a description of the various scopes.
In JDK 1.5 we use the @Aspect annotation as follows:
package com.mypackage; import org.jboss.aop.Aspect; import org.jboss.aop.advice.Scope; import org.jboss.aop.joinpoint.Invocation; @Aspect (scope = Scope.PER_VM) public class MyAspect { public Object myAdvice(Invocation invocation) }
And in JDK 1.4.2:
package com.mypackage; /** * @@Aspect (scope = Scope.PER_VM) */ public class MyAspect { public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } }
The name of the class (in this case com.mypackage.MyAspect) gets used as the internal name of the aspect. The equivalent using XML configuration would be:
<aop> <aspect class="com.mypackage.MyAspect" scope="PER_VM"/> </aop>
To mark a class as an interceptor or an aspect factory you annotate it with the @InterceptorDef annotation. The class must either implement the org.jboss.aop.advice.Interceptor interface or the org.jboss.aop.advice.AspectFactory interface.
The declaration of org.jboss.aop.InterceptorDef is:
package org.jboss.aop; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { Scope scope() default Scope.PER_VM; }
The same Scope enum is used as for Aspect. The following examples use the @Bind annotation, which will be described in more detail below.
In JDK 1.5 we use the @InterceptorDef annotation to mark an Interceptor as follows:
package com.mypackage; import org.jboss.aop.Bind; import org.jboss.aop.InterceptorDef; import org.jboss.aop.advice.Interceptor; @InterceptorDef (scope = Scope.PER_VM) @Bind (pointcut="execution("* com.blah.Test->test(..)") public class MyInterceptor implements Interceptor { public Object invoke(Invocation invocation)throws Throwable { return invocation.invokeNext(); } }
And in JDK 1.4.2:
package com.mypackage; /** * @@org.jboss.aop.InterceptorDef (scope = org.jboss.aop.advice.Scope.PER_VM) * @@org.jboss.aop.Bind (pointcut="execution("* com.blah.Test->test(..)") */ public class MyInterceptor implements Interceptor { public Object invoke(Invocation invocation)throws Throwable { return invocation.invokeNext(); } }
The name of the class (in this case com.mypackage.MyInterceptor) gets used as the class name of the interceptor. The equivalent using XML configuration would be:
<aop> <interceptor class="com.mypackage.MyInterceptor" scope="PER_VM"/> </aop>
In JDK 1.5 the @IntroductionDef annotation is used to mark an AspectFactory as follows:
package com.mypackage; import org.jboss.aop.advice.AspectFactory; @InterceptorDef (scope=org.jboss.aop.advice.Scope.PER_VM) @Bind (pointcut="execution("* com.blah.Test->test2(..)") public class MyInterceptorFactory implements AspectFactory { //Implemented methods left out for brevity }
And in JDK 1.4.2:
package com.mypackage; /** * @@org.jboss.aop.InterceptorDef (scope=org.jboss.aop.advice.Scope.PER_VM) * @@org.jboss.aop.Bind (pointcut="execution("* com.blah.Test->test2(..)") */ public class MyInterceptorFactory implements AspectFactory { //Implemented methods left out for brevity }
The name of the class (in this case com.mypackage.MyInterceptorFactory) gets used as the factory name of the aspect factory. The equivalent using XML configuration would be:
<aop> <interceptor factory="com.mypackage.MyInterceptorFactory" scope="PER_VM"/> </aop>
To define a named pointcut you annotate a field within an @Aspect or @InterceptorDef annotated class with @PointcutDef. @PointcutDef only applies to fields and is not recognised outside @Aspect or @InterceptorDef annotated classes.
The declaration of org.jboss.aop.PointcutDef is:
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PointcutDef { String value(); }
@PointcutDef takes only one value, a valid pointcut expression. The name of the pointcut used internally and when yo want to reference it is:
<name of @Aspect/@InterceptorDef annotated class>.<name of @PointcutDef annotated field>
An example of an aspect class containing a named pointcut which it references from a bindng's pointcut expression in JDK 1.5:
package com.mypackage; import org.jboss.aop.PointcutDef; import org.jboss.aop.pointcut.Pointcut; @Aspect (scope = Scope.PER_VM) public class MyAspect { @PointcutDef ("(execution("* org.blah.Foo->someMethod()) OR \ execution("* org.blah.Foo->otherMethod()))") public static Pointcut fooMethods; public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } }
It is worth noting that named pointcuts can be referenced in pointcut expressions outside the class they are declared in (if the annotated fields are declared public of course!).
The same example in JDK 1.4.2:
package com.mypackage; import org.jboss.aop.pointcut.Pointcut; /** * @@org.jboss.aop.Aspect (scope = Scope.PER_VM) */ public class MyAspect { /** * @@org.jboss.aop.PointcutDef ("(execution("* org.blah.Foo->someMethod()) \ OR execution("* org.blah.Foo->otherMethod()))") */ public static Pointcut fooMethods; public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } }
Using XML configuration this would be:
<aop> <aspect class="com.mypackage.MyAspect" scope="PER_VM"/> <pointcut name="com.mypackage.MyAspect.fooMethods" expr="(execution("* org.blah.Foo->someMethod()) OR \ execution("* org.blah.Foo->otherMethod()))" /> </aop>
To create a binding to an advice method from an aspect class, you annotate the advice method with @Bind. To create a binding to an Interceptor or AspectFactory, you annotate the class itself with @Bind since Interceptors only contain one advice (the invoke() method). The @Bind annotation will only be recognised in the situations just mentioned.
The declaration of org.jboss.aop.Bind is:
package org.jboss.aop; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Bind { String pointcut(); String cflow() default ""; }
The @Bind annotation takes two parameters:
In the case of a binding to an advice in an aspect class, the internal name of the binding becomes:
<name of the aspect class>.<the name of the advice method>
In the case of a binding to an Interceptor or AspectFactory implementation, the internal name of the binding becomes:
<name of the Interceptor/AspectFactory implementation class>
An example of a binding using an advice method in an aspect class in JDK 1.5:
package com.mypackage; import org.jboss.aop.Bind; @Aspect (scope = Scope.PER_VM) public class MyAspect { @PointcutDef ("(execution("* org.blah.Foo->someMethod()) \ OR execution("* org.blah.Foo->otherMethod()))") public static Pointcut fooMethods; @Bind (pointcut="com.mypackage.MyAspect.fooMethods") public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } @Bind (pointcut="execution("* org.blah.Bar->someMethod())") public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } }
And in JDK 1.4.2:
package com.mypackage; /** * @@org.jboss.aop.Aspect (scope = Scope.PER_VM) */ public class MyAspect { /** * @@org.jboss.aop.PointcutDef ("(execution("* org.blah.Foo->someMethod()) \ OR execution("* org.blah.Foo->otherMethod()))") */ public static Pointcut fooMethods; /** * @@org.jboss.aop.Bind (pointcut="com.mypackage.MyAspect.fooMethods") */ public Object myAdvice(Invocation invocation) { return invocation.invokeNext(); } /** * @@org.jboss.aop.Bind (pointcut="execution("* org.blah.Bar->someMethod())") */ public Object otherAdvice(Invocation invocation) { return invocation.invokeNext(); } }
The equivalent using XML configuration would be:
<aop> <aspect class="com.mypackage.MyAspect" scope="PER_VM"/> <pointcut name="com.mypackage.MyAspect.fooMethods" expr="(execution("* org.blah.Foo->someMethod()) OR \ execution("* org.blah.Foo->otherMethod()))" /> <bind pointcut="com.mypackage.MyAspect.fooMethods"> <advice name="myAdvice" aspect="com.mypackage.MyAspect"> </bind> <bind pointcut="execution("* org.blah.Bar->someMethod())"> <advice name="otherAdvice" aspect="com.mypackage.MyAspect"> </bind> </aop>
Revisiting the examples above in the @InterceptorDef section, now that we know what @Bind means, the equivalent using XML configuration would be:
<aop> <interceptor class="com.mypackage.MyInterceptor" scope="PER_VM"/> <interceptor factory="com.mypackage.MyInterceptorFactory" scope="PER_VM"/> <bind pointcut="execution("* com.blah.Test->test2(..)"> <interceptor-ref name="com.mypackage.MyInterceptor"/> </bind> <bind pointcut="execution("* com.blah.Test->test2(..)"> <interceptor-ref name="com.mypackage.MyInterceptorFactory"/> </bind> </aop>
Interface introductions can be done using the @Introduction annotation. Only fields within a class annotated with @Aspect or @InterceptorDef can be annotated with @Introduction.
The declaration of org.jboss.aop.Introduction:
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Introduction { Class target() default java.lang.Class.class; String typeExpression() default ""; Class[] interfaces(); }
The parameters of @Introduction are:
target or typeExpression has to be specified, but not both.
This is how to use @Introduction in JDK 1.5:
package com.mypackage; import org.jboss.aop.Introduction; @Aspect (scope = Scope.PER_VM) public class IntroAspect { @Introduction (target=com.blah.SomeClass.class, \ interfaces={java.io.Serializable.class}) public static Object pojoNoInterfacesIntro; }
And in JDK 1.4.2:
package com.mypackage; /* * @@org.jboss.aop.Aspect (scope = Scope.PER_VM) */ public class IntroAspect { /* * @org.jboss.aop.Introduction (target=com.blah.SomeClass, \ interfaces={java.io.Serializable}) */ public static Object pojoNoInterfacesIntro; }
Notice the slight difference in the JDK 1.4.2 annotation, the class values don't have the ".class" suffix.
This means make com.blah.SomeClass.class implement the java.io.Serializable interface. The equivalent configured via XML would be:
<introduction class="com.blah.SomeClass.class"> <interfaces> java.io.Serializable </interfaces> </introduction>
Sometimes when we want to introduce/force a new class to implement an interface, that interface introduces new methods to a class. The class needs to implement these methods to be valid. In these cases a mixin class is used. The mixin class must implement the methods specified by the interface(s) and the main class can then implement these methods and delegate to the mixin class.
Mixins are created using the @Mixin annotation. Only methods within a class annotated with @Aspect or @InterceptorDef can be annotated with @Mixin. The annotated method has
The declaration of org.jboss.aop.Mixin:
package org.jboss.aop; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Mixin { Class target() default java.lang.Class.class; String typeExpression() default ""; Class[] interfaces(); boolean isTransient() default true; }
The parameters of @Mixin are:
target or typeExpression has to be specified, but not both.
An example aspect using @Mixin in JDK 1.5:
package com.mypackage; import org.jboss.aop.Mixin; import com.mypackage.POJO; @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) public class IntroductionAspect { @Mixin (target=com.mypackage.POJO.class, interfaces={java.io.Externalizable.class}) public static ExternalizableMixin createExternalizableMixin(POJO pojo) { return new ExternalizableMixin(pojo); } }
Here's the JDK 1.4.2 version:
package com.mypackage; import org.jboss.aop.Mixin; import com.mypackage.POJO; /** * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) */ public class IntroductionAspect { /** * @org.jboss.aop.Mixin (target=com.mypackage.POJO.class, \ interfaces={java.io.Externalizable.class}) */ public static ExternalizableMixin createExternalizableMixin(POJO pojo) { return new ExternalizableMixin(pojo); } }
Since this is slightly more complex than the previous examples we have seen, the POJO and ExternalizableMixin classes are included here.
package com.mypackage; public class POJO { String stuff; }
package com.mypackage; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class ExternalizableMixin implements Externalizable { POJO pojo; public ExternalizableMixin(POJO pojo) { this.pojo = pojo; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { pojo.stuff = in.readUTF(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(pojo.stuff); } }
This has the same effect as the following XML configuration:
<introduction classs="com.mypackage.POJO"> <mixin transient="true"> <interfaces> java.io.Externalizable </interfaces> <class>com.mypackage.ExternalizableMixin</class> <construction>IntroductionAspect.createExternalizableMixin(this)</construction> </mixin> </introduction>
To prepare a joinpoint or a set of joinpoints for DynamicAOP annotate a field with @Prepare in a class anotated with @Aspect or @InterceptorDef.
The declaration of org.jboss.aop.Prepare is:
package org.jboss.aop; @Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Prepare { String value() default ""; }
The single field value contains a pointcut expression matching one or more joinpoints.
To use @Prepare in JDK 1.5:
package com.mypackage; import org.jboss.aop.Prepare; @InterceptorDef (scope = Scope.PER_VM) @Bind (pointcut="execution("* com.blah.Test->test(..)") public class MyInterceptor2 implements Interceptor { @Prepare ("all(com.blah.DynamicPOJO)") public static Pointcut dynamicPOJO; public Object invoke(Invocation invocation)throws Throwable { return invocation.invokeNext(); } }
And in JDK 1.4.2:
package com.mypackage; /** * @@org.jboss.aop.InterceptorDef (scope = org.jboss.aop.advice.Scope.PER_VM) * @@org.jboss.aop.Bind (pointcut="execution("* com.blah.Test->test(..)") */ public class MyInterceptor2 implements Interceptor { /** * @@org.jboss.aop.Prepare ("all(com.blah.DynamicPOJO)") */ public static Pointcut dynamicPOJO; public Object invoke(Invocation invocation)throws Throwable { return invocation.invokeNext(); } }
Using XML configuration instead we would write:
<prepare expr="all(com.blah.DynamicPOJO)"/>
This simple example used an @InterceptorDef class for a bit of variety in the examples, and to reiterate that @Pointcut, @Introduction, @Mixin, @Prepare, @Typedef, @CFlow, @DynamicCFlow and @AnnotationIntroductionDef can all be used both in @InterceptorDef annotated classes AND @Aspect annotated classes. Same for @Bind, but that is a special case as mentioned above.
You can also annotate a POJO with @Prepare directly in cases where you are using Dynamic AOP, and the exact bindings are not known at instrumentation time. In this case you annotate the class itself. Here's how it is done in JDK 1.5:
package com.mypackage; import org.jboss.aop.Prepare; @Prepare ("all(this)") public class MyDynamicPOJO implements Interceptor { ... }
all(this) means the same as all(com.blah.MyDynamicPOJO), but the use of all(this) is recommended.
JDK 1.4:
package com.mypackage; import org.jboss.aop.Prepare; /** * @@org.jboss.aop.Prepare ("all(this)") */ public class MyDynamicPOJO implements Interceptor { ... }
The examples just given equate to this XML
<prepare expr="all(com.blah.MyDynamicPOJO)"/>
To summarise, when using @Prepare within an @Interceptor or @Aspect annotated class, you annotate a field within that class. When using @Prepare with a POJO you annotate the class itself.
To use a typedef, you annotate a field with @TypeDef in a class anotated with @Aspect or @InterceptorDef.
The declaration of org.jboss.aop.TypeDef:
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface TypeDef { String value(); }
The single value field takes a type expression that resolves to one or more classes. The name of the typedef used for reference and internally is:
<name of @Aspect/@InterceptorDef annotated class>.<name of @TypeDef annotated field>
Here's how to use it with JDK 1.5:
package com.mypackage; import org.jboss.aop.TypeDef; import org.jboss.aop.pointcut.Typedef; @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) public class TypedefAspect { @TypeDef ("class(com.blah.POJO)") public static Typedef myTypedef; @Bind (pointcut="execution(* \ $typedef{com.mypackage.TypedefAspect.myTypedef}->methodWithTypedef())") public Object typedefAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
And with JDK 1.4.2:
package com.mypackage; import org.jboss.aop.TypeDef; import org.jboss.aop.pointcut.Typedef; /** * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) */ public class TypedefAspect { /** * @@org.jboss.aop.TypeDef ("class(com.blah.POJO)") */ public static Typedef myTypedef; /** * @@org.jboss.aop.Bind (pointcut="execution(* \ $typedef{com.mypackage.TypedefAspect.myTypedef}->methodWithTypedef())") */ public Object typedefAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
The equivalent using XML configuration would be:
<aop> <aspect class="com.mypackage.TypedefAspect" scope="PER>VM"/> <typedef name="com.mypackage.TypedefAspect.myTypedef" expr="class(com.blah.POJO)"/> <bind pointcut="execution(* \ $typedef{com.mypackage.TypedefAspect.myTypedef}->methodWithTypedef())" > <advice name="typedefAdvice" aspect="com.mypackage.TypedefAspect"/> </bind> </aop>
To create a CFlow stack, you annotate a field with @CFlowDef in a class anotated with @Aspect or @InterceptorDef. The declaration of org.jboss.aop.CFlowStackDef is:
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface CFlowStackDef { CFlowDef[] cflows(); }
In turn the declaration of org.jboss.aop.CFlowDef is:
package org.jboss.aop; public @interface CFlowDef { boolean called(); String expr(); }
The parameters of @CFlowDef are:
The name of the CFlowStackDef used for reference and internally is:
<name of @Aspect/@InterceptorDef annotated class>.<name of @CFlowStackDef annotated field>
CFlowStackDef is used like this in JDK 1.5:
package com.mypackage; import org.jboss.aop.CFlowStackDef; import org.jboss.aop.pointcut.CFlowStack; @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) public class CFlowAspect { @CFlowStackDef (cflows={@CFlowDef(expr= "void com.blah.POJO->cflowMethod1()", \ called=false), @CFlowDef(expr = "void com.blah.POJO->cflowMethod2()", \ called=true)}) public static CFlowStack cfNot1And2Stack; @Bind (pointcut="execution(void com.blah.POJO*->privMethod())", \ cflow="com.mypackage.CFlowAspect.cfNot1And2Stack") public Object cflowAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
And in 1.4.2:
package com.mypackage; import org.jboss.aop.pointcut.CFlowStack; /** * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) */ public class CFlowAspect { /** * @@org.jboss.aop.CFlowStackDef (cflows={@org.jboss.aop.CFlowDef \ (expr= "void com.blah.POJO->cflowMethod1()", called=false), \ @org.jboss.aop.CFlowDef (expr = "void com.blah.POJO->cflowMethod2()", \ called=true)}) */ public static CFlowStack cfNot1And2Stack; /** * @@org.jboss.aop.Bind (pointcut="execution(void com.blah.POJO*->privMethod())", \ cflow="com.mypackage.CFlowAspect.cfNot1And2Stack") */ public Object cflowAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
The above means the same as this XML:
<aop> <cflow-stack name="com.mypackage.CFlowAspect.cfNot1And2Stack"> <called expr="void com.blah.POJO->cflowMethod1()"/> <not-called expr="void com.blah.POJO->cflowMethod2()"/> </cflow-stack> </aop>
To create a dynamic CFlow you annotate a class implementing org.jboss.aop.pointcut.DynamicCFlow with @DynamicCFlowDef. The declaration of @org.jboss.aop.DynamicCFlowDef is:
package org.jboss.aop; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DynamicCFlowDef { }
Here is a @DynamicCFlow annotated class in JDK 1.5:
package com.mypackage; import org.jboss.aop.DynamicCFlowDef; import org.jboss.aop.pointcut.DynamicCFlow; @DynamicCFlowDef public class MyDynamicCFlow implements DynamicCFlow { public static boolean execute = false; public boolean shouldExecute(Invocation invocation) { return execute; } }
And the same in JDK 1.4.2:
package com.mypackage; import org.jboss.aop.pointcut.DynamicCFlow; /** * @org.jboss.aop.DynamicCFlowDef */ public class MyDynamicCFlow implements DynamicCFlow { public static boolean execute = false; public boolean shouldExecute(Invocation invocation) { return execute; } }
The name of the @DynamicCFlowDef annotated class gets used as the name of the cflow for references.
To use the dynamic cflow we just defined in JDK 1.5:
package com.mypackage; @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) public class CFlowAspect { @Bind (pointcut="execution(void com.blah.POJO->someMethod())", \ cflow="com.mypackage.MyDynamicCFlow") public Object cflowAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
To use the dynamic cflow we just defined in JDK 1.5:
package com.mypackage; /** * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) */ public class CFlowAspect { /** * @@org.jboss.aop.Bind (pointcut="execution(void com.blah.POJO->someMethod())", \ cflow="com.mypackage.MyDynamicCFlow") */ public Object cflowAdvice(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
You can introduce annotations by annotating a field with the @AnnotationIntroductionDef in a class anotated with @Aspect or @InterceptorDef. The declaration of org.jboss.aop.AnnotationIntroductionDef is:
package org.jboss.aop; @Target (ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationIntroductionDef { String expr(); boolean invisible(); String annotation(); }
The parameters of @AnnotationIntroductionDef are:
The listings below make use of an annotation called @com.mypackage.MyAnnotation:
package com.mypackage; public interface MyAnnotation { String string(); int integer(); boolean bool(); }
What its parameters mean is not very important for our purpose.
The use of @AnnotationIntroductionDef in JDK 1.5:
package com.mypackage; import org.jboss.aop.AnnotationIntroductionDef: import org.jboss.aop.introduction.AnnotationIntroduction; @.InterceptorDef (scope=org.jboss.aop.advice.Scope.PER_VM) @org.jboss.aop.Bind (pointcut="all(com.blah.SomePOJO)") public class IntroducedAnnotationInterceptor implements Interceptor { @org.jboss.aop.AnnotationIntroductionDef \ (expr="method(* com.blah.SomePOJO->annotationIntroductionMethod())", \ invisible=false, \ annotation="@com.mypackage.MyAnnotation \ (string='hello', integer=5, bool=true)") public static AnnotationIntroduction annotationIntroduction; public String getName() { return "IntroducedAnnotationInterceptor"; } public Object invoke(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
Note that the reference to @com.mypackage.MyAnnotation must use the fully qualified class name, and that the value for its string parameter uses single quotes.
The use of @AnnotationIntroductionDef in JDK 1.4.2:
package com.mypackage; import org.jboss.aop.introduction.AnnotationIntroduction; /** * @@org.jboss.aop.InterceptorDef (scope=org.jboss.aop.advice.Scope.PER_VM) * @@org.jboss.aop.Bind (pointcut="all(com.blah.SomePOJO)") */ public class IntroducedAnnotationInterceptor implements Interceptor { /** * @@org.jboss.aop.AnnotationIntroductionDef \ (expr="method(* com.blah.SomePOJO->annotationIntroductionMethod())", \ invisible=false, \ annotation="@com.mypackage.MyAnnotation \ (string='hello', integer=5, bool=true)") */ public static AnnotationIntroduction annotationIntroduction; public String getName() { return "IntroducedAnnotationInterceptor"; } public Object invoke(Invocation invocation) throws Throwable { return invocation.invokeNext(); } }
Note that the reference to only uses one '@', and that the value for its string parameter uses single quotes.
The previous listings are the same as this XML configuration:
<annotation-introduction expr="method(* com.blah.SomePOJO->annotationIntroductionMethod()) invisible="false" > @com.mypackage.MyAnnotation (string="hello", integer=5, bool=true) </annotation-introduction>
You can declare precedence by annotating a class with @Precedence, and then annotate fields where the types are the various Interfaces/Aspects you want to sort. You annotate fields where the type is an interceptor with @PrecedenceInterceptor. When the type is an aspect class, you annotate the field with @PrecedenceAdvice. The definitions of org.jboss.aop.Precedence, org.jboss.aop.PrecedenceInterceptor and org.jboss.aop.PrecedenceAdvice are
package org.jboss.aop; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Precedence { }
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PrecedenceInterceptor { }
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PrecedenceAdvice { String value(); }
The value() attribute of PrecedenceAdvice is the name of the advice method to use.
The example shown below declares a relative sort order where org.acme.Interceptor must always be invoked before org.acme.Aspect.advice1() which must be invoked before org.acme.Aspect.advice2(). Here is the JDK 1.5 version:
import org.jboss.aop.Precedence; import org.jboss.aop.PrecedenceAdvice; @Precedence public class MyPrecedence { @PrecedenceInterceptor org.acme.Interceptor intercept; @PrecedenceAdvice ("advice1") org.acme.Aspect precAdvice1; @PrecedenceAdvice ("advice2") org.acme.Aspect precAdvice2; }
And the JDK 1.4 version:
/** * @@org.jboss.aop.Precedence */ public class MyPrecedence { /** * @@org.jboss.aop.PrecedenceInterceptor */ org.acme.Interceptor intercept; /** * @@org.jboss.aop.PrecedenceAdvice ("advice1") */ org.acme.Aspect precAdvice1; /** * @@org.jboss.aop.PrecedenceAdvice ("advice2") */ org.acme.Aspect precAdvice2; }
The ordering of interceptors/advices defined via annotations that have no precedence defined, is arbitrary.
You can declare checks to be enforced at instrumentation time. They take a pointcut and a message. If the pointcut is matched, the message is printed out. To use this with annotations, annotate fields with DeclareWarning or DeclareError within a class annotated with @Aspect or @InterceptorDef. The definitions of org.jboss.aop.DeclareError and org.jboss.aop.DeclareWarning are:
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DeclareWarning { String expr(); String msg(); }
package org.jboss.aop; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DeclareError { String expr(); String msg(); }
For both: the expr() attribute is a pointcut expression that should not occur, and the msg() attribute is the message to print out if a match is found for the pointcut. If you use DeclareWarning instrumentation/your application will simply continue having printed the message you supplied. In the case of DeclareError, the message is logged and an error is thrown, causing instrumentation/your application to stop. Here is an example in JDK 1.5
import org.jboss.aop.Aspect; import org.jboss.aop.pointcut.Pointcut; import org.jboss.aop.DeclareError; import org.jboss.aop.DeclareWarning; @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) public class DeclareAspect { @DeclareWarning (expr="class($instanceof{VehicleDAO}) AND \ !has(public void *->save())", \ msg="All VehicleDAO subclasses must override the save() method.") Pointcut warning; @DeclareError (expr="call(* org.acme.businesslayer.*->*(..)) \ AND within(org.acme.datalayer.*)", \ msg="Data layer classes should not call up to the business layer") Pointcut error; }
And in JDK 1.4:
import org.jboss.aop.pointcut.Pointcut; /** * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM) */ public class DeclareAspect { /** * @@org.jboss.aop.DeclareWarning (expr="class($instanceof{VehicleDAO}) AND \ !has(public void *->save())", \ msg="All VehicleDAO subclasses must override the save() method.") */ Pointcut warning; /** * @@org.jboss.aop.DeclareError (expr="call(* org.acme.businesslayer.*->*(..)) \ AND within(org.acme.datalayer.*)", \ msg="Data layer classes should not call up to the business layer") */ Pointcut error; }