Here are some simple examples to get you going.
All examples together with ready to execute tests are to be found in the
source distribution under
src/samples/examples
. They are all defined using the same definition file
aop.xml
This aspect implements a simple caching service. It caches the results from the method invocations that are picked out by the pointcuts mapped to the advice.
To run the example type:
ant samples:caching
/** * @Aspect perInstance */ public class CachingAspect { /** * The cross-cutting info. */ private final CrossCuttingInfo m_info; /** * We are interested in cross-cutting info, therefore we have added a constructor that takes a cross-cutting info * instance as its only parameter. * * @param info the cross-cutting info */ public CachingAspect(final CrossCuttingInfo info) { m_info = info; } /** * @Before call(int examples.caching.Pi.getPiDecimal(int)) && * withincode(int examples.caching.main(String[])) */ public void invocationCounter(final JoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature)joinPoint.getSignature(); CacheStatistics.addMethodInvocation( signature.getName(), signature.getParameterTypes() ); } /** * @Around execution(int examples.caching.Pi.getPiDecimal(int)) */ public Object cache(final JoinPoint joinPoint) throws Throwable { MethodRtti rtti = (MethodRtti)joinPoint.getRtti(); final Long hash = new Long(calculateHash(rtti)); final Object cachedResult = m_cache.get(hash); if (cachedResult != null) { System.out.println("using cache"); CacheStatistics.addCacheInvocation(rtti.getName(), rtti.getParameterTypes()); System.out.println("parameter: timeout = " + m_info.getParameter("timeout")); return cachedResult; } final Object result = joinPoint.proceed(); m_cache.put(hash, result); return result; } // ============ Utility methods ============ ... protected Map m_cache = new HashMap(); }
This aspect implements a simple logging service. It logs the entry and exit of the methods that are picked out by the pointcuts mapped to the advice. In this simple example we are only using a small subset of all the metadata available from the join point.
To run the example type:
ant samples:logging
It is possible to run this sample using offline weaving thru
ant samples:offline:logging
An abstract aspect is provided. By extending the abstract aspect we just need to define the pointcuts to refine the metadata that defines the pointcuts where logging concern has to be bounded. Note that no pointcut needs to be defined in the abstract aspect.
public abstract class AbstractLoggingAspect { private int m_level = 0; /** * @Around methodsToLog1 || methodsToLog2 || methodsToLog3 */ public Object logMethod(final JoinPoint joinPoint) throws Throwable { MemberSignature signature = (MemberSignature)joinPoint.getSignature(); indent(); System.out.println("--> " + joinPoint.getTargetClass().getName() + "::" + signature.getName()); m_level++; final Object result = joinPoint.proceed(); m_level--; indent(); System.out.println("<-- " + joinPoint.getTargetClass().getName() + "::" + signature.getName()); return result; } /** * @Before logSet || logGet */ public void logEntry(final JoinPoint joinPoint) throws Throwable { MemberSignature signature = (MemberSignature)joinPoint.getSignature(); System.out.println("ENTER: " + joinPoint.getTargetClass().getName() + "::" + signature.getName()); } /** * @After logSet || logGet */ public void logExit(final JoinPoint joinPoint) throws Throwable { MemberSignature signature = (MemberSignature)joinPoint.getSignature(); System.out.println("EXIT: " + joinPoint.getTargetClass().getName() + "::" + signature.getName()); } private void indent() { for (int i = 0; i < m_level; i++) { System.out.print(" "); } } } /** * The concrete Aspect just defines the pointcut following the naming convention * the abstract Aspect has defined. */ public class LoggingAspect extends AbstractLoggingAspect { // ============ Pointcuts ============ /** * @Expression execution(* examples.logging.Target.toLog1(..)) */ Pointcut methodsToLog1; /** * @Expression execution(* examples.logging.Target.toLog2(..)) */ Pointcut methodsToLog2; /** * @Expression execution(* examples.logging.Target.toLog3(..)) */ Pointcut methodsToLog3; /** * @Expression get(int examples.logging.Target.m_*) */ Pointcut logGet; /** * @Expression set(int examples.logging.Target.m_*) */ Pointcut logSet; }
This example shows both how implementation introductions (Mixins) are implemented.
To run the example type:
ant samples:introduction
The example makes use of mixin inheritance within aspect
inheritance. Moreover, the concrete aspect hides the introduced interface by using
the
implicit interface lookup (introduced interface are looked for in the mixin
implementation hierarchy).
You can also note the deployment models. The mixin is perInstance whereas the aspect is perClass,
thus you have to call the right methods to retrieve the target instance (since there is none attached to the aspect).
Note: if you add more than one
Introduction
to a class
then
you have to make sure that the names of the methods
do not collide.
public abstract class AbstractIntroductionAspect { /** * The Introduce doclet is not necessary here. This aspect provides a half completed mixin impl (abstract one) */ public static abstract class MyImpl implements Mixin { public String sayHello1() { return "Hello World!"; } } } public class IntroductionAspect extends AbstractIntroductionAspect { /** * @Mixin(pointcut="within(examples.introduction.Target)", * deploymentModel="perInstance") */ public static class MyConcreteImpl extends MyImpl { /** * The cross-cutting info. */ private final CrossCuttingInfo m_info; /** * We are interested in cross-cutting info, therefore we have added a constructor that takes a cross-cutting infor * instance as its only parameter. * * @param info the cross-cutting info */ public MyConcreteImpl(final CrossCuttingInfo info) { m_info = info; } public String sayHello2() { System.out.println("mixin target class: " + m_info.getMixinTargetClass(this)); System.out.println("mixin target instance: " + m_info.getMixinTargetInstance(this)); return "Hello World! Hello World!"; } } }
Now you will be able to invoke your
Introduction
like this:
public class Target { ... System.out.println("The mixin says: " + ((Mixin)this).sayHello2()); ... }
This example shows how control flow (cflow) pointcuts are implemented.
To run the example type:
ant samples:cflow
In this example we have two methods
step1
and
step2
in which
step1
calls
step2
.
The advices at the step2
@Execution
pointcut will only be triggered if we are in the
control flow of the
@cflow
pointcut called cflowPointcut, e.g. the
method
step1
. Otherwise the advice should be skipped.
Note the
&&
expression to express the cflow algebraic.
public class CFlowAspect { /** * @Around cflow(call(void examples.cflow.Target.step1())) && execution(void examples.cflow.Target.step2()) */ public Object logMethod(final JoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(); System.out.println(" - invoking advice triggered by step2"); return result; } }
This example shows how catch clauses be intercepted with
@Handler
pointcut.
To run the example type:
ant samples:exception
public class ExceptionHandler { /** * @Before handler(java.lang.Exception+) && * withincode(public static void examples.exception.Target.main(String[])) */ public void handleException(final JoinPoint joinPoint) throws Throwable { // handle the exception CatchClauseRtti rtti = (CatchClauseRtti)joinPoint.getRtti(); Exception e = (Exception)rtti.getParameterValue(); ... } }
This examples shows Annotation driven AOP - that is matching on Annotations. The current samples relies on the provided Java 1.4 strongly typed annotations framework but the same concepts can easily be applied for Java 5 Annotations (as supported in AspectWerkz 2.0).
To run the example type:
and samples:annotation
or ant samples:offline:annotation
to see it running using
post compilation (offline weaving instead of load time weaving).
What do we want to achieve ?
We want to apply advices on method not based on their name or signature but based on the annotations. Given the
following sample class, we would like to apply an advice for all methods annotated with @examples.annotation.AnnotationA
and another advice for all methods annotated with bot @examples.annotation.AnnotationA
and @examples.annotation.AnnotationB
:
public class Target { /** * @examples.annotation.AnnotationA * @examples.annotation.AnnotationB */ public void targetAB() { System.out.println("Target.target AB "); } /** * @examples.annotation.AnnotationA */ public void targetA() { System.out.println("Target.target A"); } public void target() { System.out.println("Target.target"); } }
Annotation handling for Java 1.4
Since we are using Java 1.4 in this sample, the annotations appears in JavaDoc. Nevertheless, AspectWerkz provides a
way to treat those annotation as in Java 5 and have runtime access to them thru a strongly typed API
(read here).
In this sample, the annotations do not have any particular values. We will then use untyped annotations.
We first need to run the AnnotationC
compiler so that those Annotations gets self-embedded in
the compiled class .class
file. Before doing so, we need to write our property file to say to the
system what are the annotations used (and distinguished those from @author
for example, that we don't
want at runtime (similar to the retention policy in Java 5).
# # annotation.properties file # # examples.annotation are untyped, we don't need a proxy examples.annotation.AnnotationA examples.annotation.AnnotationB
-custom ...annotation.properties
argument to point to the annotation properties file.
<target name="samples:annotationc"> <java fork="true" classname="org.codehaus.aspectwerkz.annotation.AnnotationC"> <classpath> <path refid="project.class.path"/> <pathelement path="${samples.classes}"/> </classpath> <arg line="-verbose -src ${basedir}/src/samples -classes ${basedir}/target/samples-classes -custom ${basedir}/src/samples/annotation.properties"/> </java> </target>
Aspect that match on those annotations Now we need to write our pointcuts and advice that match on those annotations. The syntax is straightforward:
public static class AnnotationMatchAspect { /** * @Before execution(@examples.annotation.AnnotationA * examples.annotation.Target.*(..)) * @param jp */ public void beforeA(JoinPoint jp) { System.out.println("Target$AnnotationMatchAspect.beforeA"); } /** * @Before execution(@examples.annotation.AnnotationB * examples.annotation.Target.*(..)) * @param jp */ public void beforeB(JoinPoint jp) { System.out.println("Target$AnnotationMatchAspect.beforeB"); } }
Running the sample
Since we are using an annotation defined aspect, we need to run AnnotationC
on the aspect class
itself to take care of the @Before
and alike AspectWerkz provided annotations.
It is possible to run AnnotationC once on both the aspect(s) and the target application classes.
Then we need to write our little aop.xml
file to tell the framework which aspect to use. We could redefine or define
new pointcuts in this XML as well (see here)
For your convenience, everything is wrapped thru an ant target.
Run ant samples:annotation
What about offline mode ?
If we wanted to use offline mode, the process would be the same: AnnotationC on both the aspect(s) [if needed] and the
target application classes, then invoke the AspectWerkzC
post compiler, and then run your class as usual, passing the aop.xml file.
Run ant samples:offline:annotation
A sample with Java 5 Annotations defined aspects can be run from
ant samples:jdk5:callback
. For it to work you will need to
define ASPECTWERKZ_HOME
as for others samples, and have the JDK 5
environment set up as your default java.
The source code for this sample is in src/jdk15/samples/examples/callback
and illustrates how to define an Aspect using Java 5 annotations.
Here is the XML deployment descriptor file for the aspect examples above.
<!DOCTYPE aspectwerkz PUBLIC "-//AspectWerkz//DTD//EN" "http://aspectwerkz.codehaus.org/dtd/aspectwerkz2.dtd"> <aspectwerkz> <system id="samples"> <package name="examples"> <aspect class="caching.CachingAspect"> <param name="timeout" value="10"/> </aspect> <aspect class="logging.LoggingAspect"/> <aspect class="introduction.IntroductionAspect"/> <aspect class="cflow.CFlowAspect"/> <aspect class="exception.ExceptionHandlingAspect"/> <aspect class="annotation.Target$AnnotationMatchAspect"/> </package> </system> </aspectwerkz>