What is important to understand is that both the Annotation and the XML definition are both 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.
An aspect is defined in XML using the
aspect
element, which has two attributes:
class
- specifies a fully qualified class name for the aspect implementation class.
deployment-model
- specifies the depoyment model for the aspect. The default is
perJVM
if not specified
perJVM
- deploys the aspect as perJVM.
perClass
- deploys the aspect as perClass.
perInstance
- deploys the aspect as perInstance.
perThread
- deploys the aspect as perThread.
Inside the
aspect
elements you then define the poincuts, advice and introductions.
The pointcut is the construct that picks out join points, i.e. selects sets of well-defined points in the program flow.
In the XML defintion pointcuts are defined using the
pointcut
element.
When defining a
pointcut
there are two attributes that needs to be specified:
name
-
specifies the name of the pointcut. Needs to be composed of a first part also called pointcut name, unique
throughout the aspect defininition, and an optional second part called the pointcut signature :
name="<pointcut name>[(<pointcut signature>)]"
.
The signature must name its parameters (as if it was some source code).
Caution: pointcut names must only consist of these characters:
[A-Z]
,
[a-z]
,
[0-9]
and the characters
$
and
_
(not for example
-
or
/
).
pointcut signature
must be compatible with the args()
selector
when used. The abbreviations for java.lang.*
can be used as described
here.
expression
- specifies the expression for the pointcut. This is the
pattern that picks out the join points that should be included in the pointcut.
This attribute is "optional". If not specified, the <pointcut>
element must
have the expression in its body.
See the Join point selection pattern language section for a detailed description on how these patterns are written and see the Pointcut definition and Pointcut composition sections for details on how pointcuts are defined and composed.
Example of some
pointcut
definitions:
<aspect ...> <pointcut name="pc1" expression="execution(* foo.Bar.method(..))"/> <pointcut name="pc2" expression="set(* foo.Bar.m_field)"/> <pointcut name="pc3" expression="get(* foo.Bar.m_field)"/> <pointcut name="pc4" expression="handler(java.lang.Exception)AND withincode(foo.bar.*.new(..))"/> <pointcut name="pc5" expression="call(String foo.Callee.method()) AND within(foo.bar.*)"/> <pointcut name="pc6" expression="cflow(execution(* Transaction.begin(..)))"/> ... <!-- do not use a pointcut name more than once (pcArg1) --> <pointcut name="pcArg1(String s)" expression="execution(* foo.Bar.method(..)) AND args(s, ..)"/> <pointcut name="pcArg1Same(String s)" expression="pc1 AND args(s, ..)"/> <pointcut name="pcArg1Other(com.Foo foo, String[] s)" expression="execution(* foo.Bar.method(..)) AND args(foo, int, .., s)"/> <!-- use element body instead --> <pointcut name="pcMultiLine"> execution(int com.Do.foo(..)) OR execution(int come.Done.bar(..)) </pointcut> </aspect>
The pointcuts can also be "system-wide", which means that you can define pointcuts outside the
aspects, within the
system
tags. These can then be used by all
aspects
in the system defintion.
In the XML defintion advice are defined using the
advice
element.
When defining the
advice
there are three attributes that
needs to be specified:
name
- the name of the advice is the name of the method in the
aspect class that implements the advice, with a optional method signature if pointcut with
signature are used. Abbreviations can be used in this signature, including for the JoinPoint
parameter.
The signature must name its parameters (as if it was some source code) like
advice3(JoinPoint jp, int i)
.
The method defined this way must exist with the same signature in the aspect class hierarchy.
type
- the type defines the type of the advice. Valid types are:
around
- defines an around advice
before
- defines an before advice
after
- defines an after advice
bind-to
- binds the advice to a
pointcut
by referencing the name of the
pointcut
or by defining an anonymous pointcut.
If the pointcut has a signature (with args()
selector), then those
must be refered as named in the advice name signature part.
Example:
<advice name="advice1" type="before" bind-to="(pc1 || pc2) AND pc3"/> <advice name="advice2" type="around" bind-to="execution(int examples.caching.Pi.getPiDecimal(int))"/> <advice name="advice3(JoinPoint jp, int i)" type="around" bind-to="pc1 AND args(i, String[], foo.Bar)"/> <advice name="advice4(org.codehaus.aspectwerkz.joinpoint.JoinPoint jp, int i)" type="around" bind-to="execution(int examples.caching.Pi.getPiDecimal(..)) AND args(i)"/>
When defining the mixin there are two attributes that needs to be specified:
class
- the class name of the mixin
bind-to
- binds the mixin to a class using the
within(...), hasmethod(...) and/or hasfield(...)
pointcuts
XML definition
<aspect ...> ... <introduce class="IntroductionTestAspect$MyImpl" bind-to="within(test.ToBeIntroduced)"/> ... </aspect>
You also have the option of passing parameters to your aspects.
This can be very convenient if you want to reuse the same
aspect but with a different configuration without using aspect inheritance.
To pass a parameter to the aspect you simply add a
param
tag to the
aspect
definition, like this:
<aspect ... > <param name="timeout" value="10"/> </aspect>
From within an
Aspect
subclass use the method
___AW_getParameter("timeout")
to retrieve
the parameter value as a String.
Package namespaces provide a way to simplify the edition of the XML definition file so that it is not necessary
to prefix all
class
with the complete package information.
You have two ways of defining a package namespace:
base-package
attribute in the
system
element. This defines a global package namespace for all elements defined within the
system
element.
package
element. This element has one attribute;
name
, which defines the package namespace.
Example
<aspectwerkz> <system id="foodemo" base-package="foo"> <!-- All classes are prefixed by 'foo' --> <package name="bar"> <!-- All classes within these 'package' tags are prefixed by 'foo.bar' --> </package> <package name="baz.buzz"> <!-- All classes within these 'package' tags are prefixed by 'foo.baz.buzz' --> </package> </system> </aspectwerkz>
Using transformation scopes you can choose to perform the transformation within certain packages only. E.g. filter out all classes from all other packages in the transformation process. This can speed up the transformation process a lot as well as assure you that only classes within the packages you have defined is getting transformed no matter how freely you define your pointcut patterns.
A transformation scope is defined using the
exclude
and
include
element which have one attribute
package
where you define the package
name. You can define as many transformation scopes as you want.
The
package
attribute supports only
.*
as an ending pattern (package and all sub package).
During the transformation process (online or offline), a class might be transformed (depending on the pointcuts) if
exclude
packages if any
include
packages if any
include
is used the selection is much more restrictive.
This selection mechanism should be used as most as possible especially in online mode since it enables an ealry filtering.
Example
<aspectwerkz> <system id="sample"> <!-- Transformations will only take place within the 'org.codehaus.package' package and its subpackages --> <include package="org.codehaus"/> <include package="org.codehaus.*"/> <!-- synonymous of org.codehaus --> <exclude package="com.oracle.*"/> ... </system> </aspectwerkz>
Each distribution comes with a bundled DTD so that it is not looked for on the web at runtime. The XML is not validated against its DTD at runtime so be cautious to provide a valid XML defintion.
When you write your XML definition file, add the following at the top of your XML to reference the latest release:
<!DOCTYPE aspectwerkz PUBLIC "-//AspectWerkz//DTD//EN" "http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">
It is possible to specify a specific released version number as well (starting with 0.8)
<!DOCTYPE aspectwerkz PUBLIC "-//AspectWerkz//DTD 1.0//EN" "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_1_0.dtd">
Here is an example where all the definitions are put together into a single
AspectWerkz
XML definition file.
<aspectwerkz> <system id="sample"> <pointcut name="systemWide" expression="execution(int com.Do.foo(..))"/> <pointcut name="mutliLine"> call(int com.Do.foo(..)) AND within(com.ToDO) </pointcut> <package name="examples"> <aspect class="caching.CachingAspect" deployment-model="perInstance"> <param name="timeout" value="10"/> <introduce class="IntroductionTestAspect$MyImpl" bind-to="within(test.ToBeIntroduced)"/> <pointcut name="callee" expression="execution(int examples.caching.Pi.getPiDecimal(int))"/> <pointcut name="caller" expression="call(int examples.caching.Pi.getPiDecimal(int)) && within(examples.caching.*)"/> <advice name="invocationCounter" type="before" bind-to="caller"/> <advice name="cache" type="around" bind-to="callee"/> <!-- use of args() --> <pointcut name="calleeArgFiltered(int i)" expression="callee AND args(i)"/> <advice name="traceArg(JoinPoint jp, int theArg)" type="around" bind-to="calleeArgFiltered(theArg)"/> </aspect> </package> </system> </aspectwerkz>