What is important to understand is that both the Annotation and the XML definition are 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 the following attributes:
class
- [mandatory] specifies a fully qualified class name for the aspect implementation class.
deployment-model
- specifies the deployment 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.
name
- optional unique name of the aspect in the system
.
It will default to the fully qualified class name of the aspect.
Inside the
aspect
elements you then define the poincuts and advice.
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 definition pointcuts are defined using the
pointcut
element.
When defining a
pointcut
there are two attributes that need to be specified:
name
- [mandatory]
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 be valid Java identifiers (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(), this() and target()
pointcuts designators
when used. The abbreviations for
java.lang.*
can be used as described
here.
expression
- [mandatory unless body is used] 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.
The deployment-scope
element within the aspect
can be used to define and name a pointcut
that will be later used to hotdeploy and undeploy Aspects.
The attributes and their meaning is the same as for the pointcut
elements.
name
- the name of the deployment scopeexpression
- the pointcut expression
The deployment-scope
element within the system
can be used to define and name a pointcut
that will be later used to hotdeploy and undeploy Aspects.
The attributes and their meaning is the same as for the pointcut
elements.
name
- the name of the deployment scopeexpression
- the pointcut expression
In the XML definition advice are defined using the
advice
element.
When defining the
advice
there are three attributes that
need to be specified:
name
- [mandatory] the name of the advice is the name of the method in the
aspect class that implements the advice, with an optional method signature if pointcut with
signature are used. Abbreviations can be used in this signature, including for the
JoinPoint
or StaticJoinPoint
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 (finally) advice
after finally
- defines an after finally advice
after returning
- defines an after returning advice
after throwing
- defines an after throwing advice
after returning(..)
- defines an after returning advice and filters out or bind the returned value
after throwing(..)
- defines an after throwing advice and filters out or bind the throwned exception
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(), this() and/or target() or after returning(..), after throwing(..)
), then those
must be refered as named in the advice name signature part.
For after returning and after throwing, you can filter out based on the returned value / exception thrown or use explicit binding.
after returning [TYPE] advice is executed if the method returns normally (without
throwing an exception) and the actual type that is returned is of the type that is specified
in the advice declaration.
If no return type is specified then it applies to all invocations that return normally, e.g. not
when throwing an exception.
<advice type="after returning(@Service *..*)" bind-to="execution(@TxRequired * *.*(..))" name="txCommitReturningService"/> <advice type="after returning(returned)" bind-to="execution(@TxRequired * *.*(..))" name="txCommitReturningService(String returned)"/>
after throwing [TYPE] advice is executed if the returns with an exception
and the actual type of the exception that is thrown is of the type that is specified
in the advice declaration.
If no exception type is specified then it applies to all invocations that returns with
an exception.
<advice type="after throwing(SecurityException+)" bind-to="execution(@TxRequired * *.*(..))" name="txRollbackOnRuntimeException"/> <advice type="after throwing(thrownedException)" bind-to="execution(@TxRequired * *.*(..))" name="txRollbackOnRuntimeException(SecurityException thrownedException)"/>
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)"/>
You 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 API
AspectContext.getParameter("timeout")
to retrieve
the parameter value as a String.
Interface only introductions are defined within the aspect
element
using an introduce
elemement.
When defining the interface only introduction the following attributes can be used:
class
- [mandatory] the class name of the introduced interface
bind-to
- [mandatory] binds the introduction to a class using the
within(...), hasmethod(...) and/or hasfield(...)
pointcuts
The introduce
element supports nested bind-to
elements.
<aspect ...> ... <introduce class="java.io.Serializable" bind-to="within(test.some.ToBeSerializable)"/> ... </aspect>
Mixin allows to introduce both interface(s) and implementation(s) of it.
The mixin
element within the system
element is then used to declare the mixin.
The mixin itself can be Annotation defined or XML defined.
The mixin
element in the XML definition (in the META-INF/aop.xml
file) can be used for
three different purposes:
class
attribute, e.g.
<mixin class="foo.bar.MyMixinImpl"/>
. See below for details.
To define the mixin using the XML definition, use the mixin
element. It has
five attributes that can be used:
class
[mandatory] - defines which class to use as the implementation class.
Has to be the fully qualified name of the class.
deployment-model
(optional) - defines the deployment model. Can be one of
perJVM
,
perClass
or perInstance
(defaults).
transient
(optional) - defines the mixin as transient
or not.
factory
(optional) - defines the custom mixin factory implementation to use
with the specific mixin.
bind-to
(optional) - defines to which class set the mixin should be applied to,
using the within(...)
, hasmethod(...)
and/or hasfield(...)
pointcuts.
Example:
<mixin class="foo.bar.MyMixinImpl"/> <mixin class="foo.bar.MyOtherMixinImpl" deployment-model="perClass" transient="true" factory="factories.MyMixinFactory" bind-to="within(*..domain.*)"/>
Package namespaces provide a way to simplify the creation/modification 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 assures you that only classes within the packages you have defined are getting transformed no matter how freely you have defined 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 early 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/aspectwerkz2.dtd">
It is possible to specify a specific released version number as well (starting with 0.8)
<!DOCTYPE aspectwerkz PUBLIC "-//AspectWerkz//DTD 2.0//EN" "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_2_0.dtd">
<!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> <deployment-scope name="mutliLineScope"> within(com.DynamicAOP) </deployment-scope> <!-- package prefix "examples." will be used for class=".." attributes of <aspect> and <mixin> elements --> <package name="examples"> <aspect class="caching.CachingAspect" deployment-model="perInstance"> <param name="timeout" value="10"/> <introduce class="IntroductionTestAspect$MarkerInterface" bind-to="within(test.mixin.perinstance.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> <mixin class="caching.MyMixin" deployment-model="perClass" bind-to="within(test.mixin.perinstance.ToBeAffectedByMixin)"/> <!-- annotation defined mixin --> <mixin class="caching.MyMixinAnnotationDefined"/> </package> </system> </aspectwerkz>