General definition issues
This section documents the pointcut language, and goes thru advanced configuration concepts like pluggable aspect container and handling multiple AOP systems.
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.
..
- used either to match any sequence of characters that
start and end with a "." (so it can be used to pick out all
types in any subpackage) or in method selectors to match
as many parameters as possible.
org.codehaus..*
will match all classes in all subpackages starting from
org.codehaus
.
* method(..)
will match all methods with
any number of parameters.
..
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.
Another specific character to express inheritance based matching will be presented further.
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>
Example of a full class pattern:
<annotations> <modifiers> <package_and_class_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.
Note that in the previous sample, foo.Bar
can be wether a class (a super class)
or an interface.
A type name pattern or subtype pattern can be followed by one or more sets of square brackets to make array type patterns. So java.lang.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.
Two extra specific abbreviations are in place:
JoinPoint
will be resolved as org.codehaus.aspectwerkz.joinpoint.JoinPoint
and StaticJoinPoint
will be resolved as org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint
.
This is usefull when dealing with complex advice signature in the XML definition.
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.
staticinitialization(<type pattern>)
- picks out join points defining class
static initialization.
execution(<method or constructor pattern>)
- picks out join points defining method
(static or member) or constructor execution.
call(<method or constructor pattern>)
- picks out join points defining method
(static or member) or constructor call.
handler(<exception type pattern>)
- picks out join points
definining a catch clause.
Valid advice for this pointcut is
before
.
within(<type pattern>)
- picks out a type set.
This pointcut can be used when you need to limit the scope on which join points you pick out.
It is really useful (and for most cases necessary) to use together with
call
and
handler
pointcuts.
It can be used with other pointcuts as well (
staticinitialization
, 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.
withincode(staticinitialization(<type pattern>))
- picks out a static class
initalizer set.
This pointcut can be used when you need to limit the scope on which join points you pick out.
Really useful (and and most cases necessary) to use together with
call
,
get
,
set
and
handler
pointcuts.
args(<type pattern or parameter name reference or greedy ".." pattern> [, <etc>]*)
- picks out parameter types and optionnaly bind them as pointcut parameters.
This pointcut has several usage forms that can be mixed together, but be carefull at not mixin several
args()
pointcut together in logical expression.
First it can be used to filter method/constructor parameters types and field set field type.
The special ".." pattern is used to match
as many parameters as possible and should only be used once.
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[])
.
Be carefull since
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.
Second it can be used to retrieve and keep references to target method/constructor parameters and field value beeing set.
For such a usage, the pointcut itself will be required to have a signature.
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.
If you are interested in getting the first and last parameter, no matter the number and types of parameters in between, the special
..
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.
Last
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).
target(<type pattern or parameter name reference>)
- picks out the target (callee) instance type and optionnaly bind it as pointcut parameter.
this(<type pattern or parameter name reference>)
- picks out the this (caller) instance type and optionnaly bind it as pointcut parameter.
this(..) and target(..) pointcut designators can be used to narrow a pointcut expression based on caller and calle types or to gain access to those in the advice signature without any casting or reflective access.
Note that they will implictly filter out callee side static methods, callee constructors on constructor call pointcut (since the callee is not available yet), and caller side static method for call pointcuts and field pointcuts.
First, when used with type patterns: target(foo.*), this(foo.Bar+), etc, they will filter respectively callee and caller types.
Second, when used with a paramter binding, they will allow to gain direct access to respectively callee (target) and caller (this) instance in advice by using advice argument binding, when combined with pointcut with signature or advice with signature (see previous part).
// this pointcut is not declared as a field since it exposes parameter binding // note use of "bar" variable name @Expression("target(bar)") void pointcut(Bar bar) {} // use the pointcut with respect to its signature // note use of "myBar" variable name @Before("execution(* *..method(..)) && pointcut(myBar)") void before(Bar myBar) { myBar.callback(); }
cflow(<pointcut expression>)
- picks out join points defining a control flow (cflow).
Note: the nested pointcut expression must not bind parameters. args(), this() and target() can be used
with typed expression only (ie statefull cflow is not yet supported). Nested cflow() can be used.
hasmethod(<method or constructor pattern>)
- picks out a class that
has at least one method or constructor that match the given pattern.
This pointcut can be used when you need to limit the scope on which join points you pick out based on
a structural information of the class in which the join point may appear. This pointcut is similar to
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.
This pointcut can be used when you need to limit the scope on which join points you pick out based on
a structural information of the class in which the join point may appear. This pointcut is similar to
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:
!
- 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.
!
also works in conjunction with modifiers.
For example:
!public * *..*.*(..)
for picking out all non-public methods.
Examples:
staticinitialization(foo..*) && within(@AnnotatedFoo) 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 * *..*.*(..))
You can define pointcuts in one aspect definition (in XML or in annotations) and then 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>
Note that you can only reference pointcut from Aspects defined within the same AOP system.
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 belong to an aspect
system
. The system provides namespace isolation and is ClassLoader aware.
Aspect systems are named in the XML definition file and each system
must have a unique name within a ClassLoader hierarchy. This is a good practice to use a package naming
convention for naming your system.
The system name (
id
) is used when accessing system at runtime, for example when redefining the aspect system.
Example:
<aspectwerkz> <system id="my.system.name"> <aspect name="MyAspect1"/> <aspect name="MyAspect2"/> ... </system> </aspectwerkz>
Sample to access the system(s) at runtime
SystemDefinition system = SystemDefinition.getDefinitionFor(classLoader, id); Set<SystemDefinition> systems = SystemDefinition.getDefinitionsFor(classLoader);
Systems follow ClassLoader rules.
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
AspectContext.getParameter("timeout")
to retrieve the parameter value as a String.
To set a new (or override) a parameter (from within an aspect) use
AspectContext.setParameter("timeout", "10")
to retrieve the parameter value as a String.
AspectWerkz
supports different deployment models,
which defines the scope of the
Aspect
and Mixin
.
The three different deployment models for Aspects 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.
The three different deployment models for Mixins are:
perInstance
- one instance per class instance.
perClass
- one instance per class.
perJVM
- one instance per JVM.
Basically the same thing as a singleton class.
The deployment model can be specified using the @Aspect
or @Mixin
annotation or using the XML definition. Refer to the sections covering
Annotation or
XML definitions.