AspectWerkz
support a fine-grained pattern
language for picking out join points.
You can utilize two types of wildcards when constructing your patterns:
*
- which is used as a regular wildcard.
Matches for example only one package level or one method parameter.
When used to match a package name, matches
at least one character. Else match
zero or more character.
..
- matches any sequence of characters that
start and end with a ".", so it can be used to pick out all
types in any subpackage. For example
org.codehaus..*
will match all classes in all subpackages starting from
org.codehaus
.
..
wildcard as the
"last" thing specified. I.e. this is
not possible:
foo.bar..test.MyClass
, but this is:
foo.bar..
. The same thing holds for method
parameters.
The patterns normally consists of a combination of a class and a method pattern or a class and a field pattern.
Example of a full method pattern:
<annotations> <modifiers> <return_type_pattern> <package_and_class_pattern>.<method_name_pattern>(<parameter_type_patterns>)
Example of a full field pattern:
<annotations> <modifiers> <field_type_pattern> <package_and_class_pattern>.<field_name_pattern>
The classes are selected by specifying a pattern that consists of:
All class patterns must follow this structure:
<annotations> <modifiers> <full_class_name>
For the class selections specify the full package name of the class along with some wildcards.
Examples:
foo.bar.*
- will match
foo.bar.FooBar2
as well as
foo.bar.FooBear
but not
foo.bar.subpackage.FooMouse
foo.*.FooBar
- will match
foo.bar.FooBar
as well as
foo.bear.FooBar
but not
foo.bear.FooBear
.
foo.*.FooB*
- will match
foo.bar.FooBar2
as well as
foo.bear.FooBear
as well as
foo.bear.FooB
.
public foo.bar.*
- will match
public static final foo.bar.FooBar
as well as
public static foo.bar.FooBar
but not
static foo.bar.FooBar
or
private foo.bar.FooBar
.
@Session foo.bar.*
- will match
@Session foo.bar.FooBar
but not
foo.bar.FooBar
or
private foo.bar.FooBar
.
foo..
- will match
The methods are selected by specifying a pattern that consists of:
All method patterns must follow this structure:
<annotations> <modifiers> <return_type> <full_method_name>(<parameter_types>)
Examples
int foo.*.Bar.method()
- will match
int method()
but not
int method(int i)
.
int *.method(*)
- will match
int Foo.method(int i)
but not
int Foo.method()
and not
int apackage.Foo.method(int i)
* method(..)
- will match
void Foo.method()
as well as
void apackage.Bar.method(int[] i)
int foo.*.*.method(*,int)
- will match
int method(String s, int i)
as well as
int method(int i1, int i2)
.
int foo.*.Bar.method(..)
- will match
int method()
as well as
int method(String s, int i)
as well as
int method(int i, double d, String s, Object o)
.
int foo.*.Bar.method(int,..)
- will match
int method(int)
as well as
int method(int i, String s)
as well as
int method(int i, double d, String s, Object o)
.
int foo.*.Bar.method(java.lang.*)
- will match
int method(String s)
as well as
int method(StringBuffer sb)
.
int foo.*.Bar.me*o*()
- will match
int method()
as well as
int metamorphosis()
and
int meo()
but not
int me()
.
* foo.*.Bar.method()
- will match
int method()
as well as
java.lang.String method()
.
java.lang.* foo.*.Bar.method()
- will match
java.lang.String Bar.method()
as well as
java.lang.StringBuffer Bar.method()
.
static int foo.*.Bar.method()
- will match
static int method()
but not
int method(int i)
.
@Transaction * foo.*.*.*(..)
- will match
@Transaction int method()
but not
void method(int i)
.
The constructors are selected by specifying a pattern that consists of:
All the patterns must follow this structure:
<annotations> <modifiers> <className>.<new>(<parameter_types>)
Examples
foo.*.Bar.new()
- will match
new Bar()
but not
new Bar(int i)
.
* new(..)
- will match
new Foo()
as well as
new apackage.Bar(int[] i)
*.new(String)
- will match
new Foo(String name)
and
new Bar(String name)
but not
new Foo()
.
The fields are selected by specifying a pattern that consists of:
All field patterns must follow this structure:
<annotations> <modifiers> <field_type> <full_field_name>
Examples
int foo.*.Bar.m_foo
- will match
int m_foo
but not
int s_foo
or
long m_foo
.
* m_field
- will match
int Foo.m_field
as well as
int[] apackage.Bar.m_field
* foo.*.Bar.m_foo
- will match
int m_foo
as well as
java.lang.String m_foo
.
java.lang.* foo.*.Bar.m_foo
- will match
java.lang.String m_foo
as well as
java.lang.StringBuffer m_foo
.
int foo.*.Bar.m_*
- will match
int m_foo
as well as
int m_bar
.
int foo.*.Bar.m_*oo*
- will match
int m_foo
as well as
int m_looser
as well as
int m_oo
.
It is possible to pick out all subtypes of a type with the "+" wildcard. The "+" wildcard follows immediately a type name pattern. So, while
* foo.Bar.*(..)
picks out all method call join points where an instance of exactly type Foo is constructed,
* foo.Bar+.*(..)
picks out all method call join points where an instance of any subtype of Foo (including Foo itself) is constructed.
A type name pattern or subtype pattern can be followed by one or more sets of square brackets to make array type patterns. So Object[] is an array type pattern, and so is foo.bar.*[][].
When picking out the return and parameter types it is
possible to use predefined abbreviations for the classes
in the
java.lang.*
and
java.util.*
packages. If you specify only the class name it will be
mapped to the full class name for the class (you cannot use patterns in abbreviations).
Abbreviations are supported for array types as well, with a dimension less or equal to 2.
String[][]
will thus be resolved as java.lang.String[][]
but
String[][][]
will not.
Examples
You can use:
String
instead of
java.lang.String
List
instead of
java.util.List
String*
instead of
java.lang.String
or
java.lang.StringBuffer
Apart from these abbreviations you always have to specify the fully qualified name of the class (along with the wildcards).
The pointcut is a construct that picks out join points, i.e. selects well-defined points in the program flow.
The following types of pointcuts are currently supported:
execution(<method or constructor pattern>)
- picks out join points defining method
(static or member) or constructor execution.
around
,
before
and
after
.
call(<method or constructor pattern>)
- picks out join points defining method
(static or member) or constructor call.
around
,
before
and
after
.
set(<field pattern>)
- picks out join points defining field modification.
around
,
before
and
after
.
get(<field pattern>)
- picks out join points defining field access.
around
,
before
and
after
.
args(<type pattern or parameter name reference or greedy ".." pattern> [, <etc>]*)
- picks out parameter types and optionnaly bind them as pointcut parameters.
args()
pointcut together in logical expression.
execution(* method(..)) AND args(String, com.Bar[])
is equivalent to execution(* method(String, com.Bar[])
though it provides a better decomposition and could be split
using pointcut references in something like myMethodsExecution AND args(String, com.Bar[])
.
call(* method(String)) AND args(String, int)
will match .. nothing !
set(* Bar.m_field) AND args(String)
will match the String Bar.m_field
field set.
myPointcut(String s)
defined with execution(* method(..)) AND args(s)
will thus
match "method(String)" and moreover will further reference the parameter value of s. The advice will use this parameter in
its own signature. void myBeforeAdvice(JoinPoint, String as)
could thus be bounded to myPointcut(as)
.
In this syntax, you have to be very cautious about the parameter name, here "s" in the pointcut signature and args() expression as well as "as" (could be s or "whatYouWant")
in the advice and the advice binding expression.
..
greedy
pattern can then be used like in
myPointcut(String argLast, int argFirst)
defined with execution(* method(..)) AND args(argFirst, .., argLast)
.
In this sample, you will notice that the pointcut signature is not required to respect the parameter order, though it is a bit error prone.
args()
can be use to both match parameter types and retain their values like in
myPointcut(String s)
defined with execution(* method(String, ..)) AND args(s, int, com.Bar, StringBuffer[][])
.
In such an expression, you will have noticed that we have to fully define the com.Bar
type, while abbreviations for java.lang.*
could be used (see the previous part for a note on abbreviations).
handler(<exception type pattern>)
- picks out join points
definining a catch clause.
before
.
cflow(<pointcut expression>)
- picks out join points defining a control flow (cflow).
AND
expression.
within(<type pattern>)
- picks out a type set.
call
and
handler
pointcuts.
execution
, get/set
) to narrow
the scope of the pointcut. For example,
execution(* method(..))
will match all methods in all classes
but execution(* method(..)) AND within(com.Foo)
will match only in the com.Foo class.
withincode(<method or constructor pattern>)
- picks out a method
or constructor set.
call
and
handler
pointcuts.
hasmethod(<method or constructor pattern>)
- picks out a class that
has at least one method or constructor that match the given pattern.
within
- that is for example: hasmethod(* com.Foo.*(..)) is equivalent to within(com.Foo) unless the com.Foo class
has no method at all.
hasfield(<field pattern>)
- picks out a class that
has at least one field that match the given pattern.
within
Pointcuts can either defined:
args()
selector. The pointcut
with a signature must be named (so that the named paramter in args() can be referenced in its signature).
AspectWerkz
supports pointcut composition, which means that poincuts can be composed.
To compose pointcut expressions you can use these logical operators:
!
or
NOT
- logical not
||
or
OR
- logical or
&&
or
AND
- logical and
Using these operators together with parenthesis you can form any kind of algebraic expression. The model is highly orthogonal since all it allows you to mix any type of pointcut when composing new pointcuts.
!
,
NOT
or
not
also works in conjunction with modifiers.
For example:
!public * *..*.*(..)
for picking out all non-public methods.
Examples:
execution(* foo.bar.Baz.*(..)) || call(* foo.bar.Baz.*(..)) (set(* foo.bar.*) || get(* foo.bar.*)) && withincode(* foo.bar.Buzz.*(..)) handler(java.lang.Exception+) && !cflow(call(* foo.bar.Buzz.(..))) call(!public !static * *..*.*(..)) call(NOT public NOT static * *..*.*(..))
You can define pointcuts in one aspect definition (in XML or in annotations) and the refer to this pointcut in another aspect definition.
This can be very useful when building up pointcut libraries that you want to use throughout the projects or multiple projects.
You are referring to the external pointcut by using the aspect name (the full
name of the aspect class if not a custom name has been defined) followed by a dot and then the
pointcut name. For example:
mylib.J2EEPointcuts.sessionBeans
Example:
<aspect class="foo.bar.MyAspect> <pointcut name="transactedSessionBeanMethods" expression="call(@Transaction * *..*.*(..)) && within(mylib.J2EEPointcuts.sessionBeans)"/> ... </aspect>
You have the possibility of providing your own aspect container implementation. This can be useful if you need to control how your aspects are instantiated. (For example if you want to have them working with an IoC container (Spring, PicoContainer etc.))
To create a custom container you only need to implement the
org.codehaus.aspectwerkz.aspect.AspectContainer
interface, but it can
be very beneficial to extend the
org.codehaus.aspectwerkz.aspect.AbstractAspectContainer
abstract base class
(and only implement the abstract method
Object createAspect()
).
You specify which aspect should use which container in the XML definition using the
container
element.
Example:
<aspect class="foo.bar.Baz" container="org.codehaus.aware.container.SpringAspectContainer"> ... </aspect>
If you don't provide a custom implementation the default one will be used.
For a complete example on how to write your own custom aspect container take a look at the SpringAspectContainer documentation (and sources).
Deployed aspects belongs to an aspect
system
. For now
AspectWerkz
support only a
single system in the whole JVM. Aspect systems are named in the XML definition file and each system
must have a unique name.
The system name (
id
) is used when accessing system at runtime, for example when redefining the aspect system.
Example:
<aspectwerkz> <system id="system name"> <aspect name="MyAspect1"/> <aspect name="MyAspect2"/> ... </system> </aspectwerkz>
Sample to access the system(s) at runtime
AspectSystem system = SystemLoader.getSystem(targetInstance); AspectSystem system = SystemLoader.getSystem(targetClass); AspectSystem system = SystemLoader.getSystem(classLoader); Collection systems = SystemLoader.getAllSystems();
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>
To retrieve the parameter (from within an aspect) use
CrossCuttingInfo.getInfo("systemId", this).getParameter("timeout")
to retrieve the parameter value as a String.
To set a new (or override) a parameter (from within an aspect) use
CrossCuttingInfo.getInfo("systemId", this).setParameter("timeout", "10")
to retrieve the parameter value as a String.
AspectWerkz
supports four different deployment models,
which defines the scope of the
Aspect
.
The four different deployment models are:
perJVM
- one sole instance per JVM.
Basically the same thing as a singleton class.
perClass
- one instance per class.
perInstance
- one instance per class instance.
perThread
- one instance per thread.
Since introduction implementation are inner classes, they have to follow a subset of the aspect's deployment model. If not specified, the introduction is deployed as the aspect that defines it. Else the following applies:
Introduction deployment model | required Aspect deployment model |
---|---|
perJVM | perJVM (default for Aspect) |
perClass | perJVM or perClass |
perInstance | perinstance or perClass or perJVM |
perThread | perThread |