Implementation issues
This section documents the programming model for Aspects, Advice and Mixins in AspectWerkz.
In
AspectWerkz
any Java class can be an aspect (or more conceptually correct, can
be defined to be
cross-cutting), which means that it will become a unit of modularity
for crosscutting concerns.
They do not have to extend any special class or implement a specific interface, but can extend any class they want.
The only requirement is that the aspect needs to have either no constructor at all (meaning implicitly a default one) or one of these two different constructors defined:
org.codehaus.aspectwerkz.AspectContext
instance
as its only parameter - this is needed if you want to retrieve information about the runtime
system, access parameters defined at deployment time, access meta-data etc.
See
Using the AspectContext class section
for details.
AspectWerkz
will try to use firstly the constructor of type 2. If not found than the default
constructor will be used. If no default constructor is found then an exception is thrown.
The aspect can then be defined using XML or annotations, see the XML definition section or the Annotation definition section for more information.
Since the Aspects are pure Java classes, you have the possibility of defining abstract aspects that you can reuse by implementing another aspect that inherits the abstract aspect.
Aspect inheritance is exactly regular class inheritance. An abstract aspect
is defined using the
abstract
keyword as a regular abstract class and is inherited
using the
extends
keyword.
Nevertheless specific rules must be observed for advices represented as methods. Indeed, the advice is likely to be invoked from the class it is applied to as a result of the weaving process. Advices should thus be public. Private or protected advices are not supported. Package private advices are not encouraged. Aspects classes should for the same purpose be public (or package private though not encouraged).
In the aspect class the advices are regular methods. The methods needs to conform to a specific signature
unless
args()
, this()
and/or target()
selectors are used in the pointcut
the advice is bounded to.
Around
advice:
public Object <name of method>(StaticJoinPoint staticJoinPoint) throws Throwable
signature.
public Object <name of method>(JoinPoint joinPoint) throws Throwable
signature.
Before
,
After
,
After Finally
,
After Returning (TYPE)
and
After Throwing (TYPE)
advice:
public void <name of method>()
signature.
public void <name of method>(StaticJoinPoint staticJoinPoint)
signature.
public void <name of method>(JoinPoint joinPoint)
signature.
Using StaticJoinPoint, JoinPoint or no JoinPoint at all is discussed in this section.
Here is a simple example of an
Around
advice. (For more examples see the
Examples section.) The
JoinPoint
object that is passed to
the method contains metadata and RTTI of the current
join point. To invoke the next advice in the chain (or the target method if there are no more advice)
simply call
joinPoint.proceed()
which will return the result from the next advice (or the target method).
@Around("myPointcut") public Object myAroundAdvice(JoinPoint joinPoint) throws Throwable { // do some stuff Object result = joinPoint.proceed(); // do some more stuff return result; }
Note: for such a simple advice, StaticJoinPoint and staticJoinPoint.proceed() would be preferable.
When using pointcut designators like args(..), this(..), target(..), or when using after returning or after throwing with a type binding, advice signature will be pointcut dependant. This allows to gain direct access to the advised method/constructor/field set arguments. The Pointcut declaration itself will have a specific signature. The bounded advice(s) is then required to have those parameters in their signature as well. This allows to avoid using the RTTI interface as well as providing strong typing and implies better performance.
Such an advice can be defined through Annotations and through XML. See the XML definition section or the Annotation definition section for more information.
The advice signature is thus depending on the pointcut signature and the use of JoinPoint, StaticJoinPoint, user defined JoinPoint or not use of it at all. A sample could look like the following:
// will match all method named "log" with a "java.lang.String" as sole parameter @Expression("execution(* log(..)) AND args(s)") Pointcut myPointcut(String s) {return null;} @Before("myPointcut(adviceArg)") public void myBeforeAdvice(JoinPoint joinPoint, String adviceArg) {...} // it is possible to have the JoinPoint at any index in the advice parameter list // though a best practice is to keep it at the first position @Around("myPointcut(adviceArg)") public Object myAroundAdvice(String adviceArg, StaticJoinPoint joinPoint) {... joinPoint.proceed(); ...} // user defined JoinPoint (could extend StaticJoinPoint as well) static interface MyJoinPoint extends JoinPoint { Object proceed(String adviceArgIdLikeToChange); } @Around("myPointcut(adviceArg)") public Object myAroundAdvice(MyJoinPoint myJoinPoint) { ... myJoinPoint.proceed("new value !!"); ... } @After("myPointcut(adviceArg)") public void myAfterAdvice(JoinPoint joinPoint, String adviceArg) {...}
In previous releases of
AspectWerkz
, we only supported one single type of
after advice. The semantics of this type was that it was always executed, regardless
of whether an exception had been thrown or if the method returned successfully.
In the new 2.x architecture we have enhanced the semantics for this type of advice and have
borrowed the semantics from
AspectJ
. So now we support three different types
of
after advice:
@AfterFinally
(same as
@After
)
@AfterReturning [TYPE]
-
@AfterReturing(pointcut)
or
@AfterReturning(type=onReturnedType, pointcut=pointcut)
@AfterThrowing [TYPE]
-
@AfterThrowing(pointcut)
or
@AfterThrowing(type=onThrownedType, pointcut=pointcut))
An
after finally
advice declaration has the same semantics as an
after
advice declaration, you can use any syntax you like.
after finally
advice are always executed, they work the same as a finally block
(meaning that they will be invoked regardless of wheter an exception has been thrown or if the
method has returned successfully).
Java 5 annotation definition
@AfterFinally("execution(@TransactionAttribute * *.*(..))") public void logTxMethods(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterFinally("execution(@TransactionAttribute * *.*(..))") */ public void logTxMethods(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after finally" bind-to="execution(@TransactionAttribute * *.*(..))" name="logTxMethods"/>
after returning [TYPE]
advice are 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.
Java 5 annotation definition
@AfterReturning( type="@Service *..*", pointcut="execution(@TransactionAttribute * *.*(..)" ) public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterReturning( * type="@Service *..*", * pointcut="execution(@TransactionAttribute * *.*(..)" * ) */ public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after returning(@Service *..*)" bind-to="execution(@TransactionAttribute * *.*(..))" name="txCommitReturningService"/>
after throwing [TYPE]
advice is executed if the advised method
returns with an exception and the actual type of the exception that was 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 return with an exception.
Java 5 annotation definition
@AfterThrowing( type="RuntimeException", pointcut="execution(@TransactionAttribute * *.*(..)" ) public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterThrowing( * type="RuntimeException", * pointcut="execution(@TransactionAttribute * *.*(..)" * ) */ public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after throwing(RuntimeException)" bind-to="execution(@TransactionAttribute * *.*(..))" name="txRollbackOnRuntimeException"/>
Each advice is passed the
JoinPoint
instance or
StaticJoinPoint
instance if it requires it. The
JoinPoint
class implements
the join point concept, i.e. a well-defined point in the program flow and provides a reflective access API
to it. The
StaticJoinPoint
class is a lightweight version without the reflective access API which
allows for a better performance (at the pros/cons of less genericity).
The
JoinPoint
instance contains static information and RTTI (runtime type information) about the join point we are
currently executing over.
The
StaticJoinPoint
instance contains only the static information.
The static information (the signature) is retrieved through one of the
Signature interfaces by invoking
getSignature()
.
While the RTTI is retrieved through one of the
RTTI interfaces
by invoking
getRtti(), getTarget(), getThis(), getCaller(), getCallee()
.
The
JoinPoint
and
StaticJoinPoint
class have a method called
proceed()
, this method is used in
Around
advice to either:
proceed()
method returns the result from the method invocation and in the
Around
advice you have the option of either returning the value returned from the
proceed()
method or faking the value and returning something else.
The
JoinPoint
class has some other interesting methods as well, see the JavaDoc for details.
Advice are methods in aspect class. The method signature must conform to some rules, depending on the pointcut where the advice is bound to, when args(..), this(..) and/or target(..) are used to gain access to join point argument / caller / callee instance.
Aside from this pointcut bounded arguments, the advice can have extra arguments of type "JoinPoint" or "StaticJoinPoint", which do not appear in the pointcut. First version of AspectWerkz were supporting only "JoinPoint" as advice argument, and use of "getRtti()" RTTI API was mandatory.
In AspectWerkz 2.x, use of "StaticJoinPoint" and/or "JoinPoint" as advice argument is optional and use of it (or no use of it at all) depends on aspect developer decision. Note that for "around" advice, in order to invoke "proceed()" properly, you will need access to either a "StaticJoinPoint" or "JoinPoint" instance).
When only "StaticJoinPoint" is used, specific optimizations are used to maximize runtime performance
since RTTI access is then guaranteed to not happen.
The following demonstrates legal syntax:
@Before("execution(* foo.Bar.method(..))") void before() {} @Before("execution(* foo.Bar.method(..))") void before(StaticJoinPoint sjp) {} @Before("execution(* foo.Bar.method(..))") void before(JoinPoint jp) {} @Around("execution(* foo.Bar.method(..))") Object around() { // possible but rather useless since then all following advices // in the chain are skipped since proceed() is not called return null; } @Around("execution(* foo.Bar.method(..))") Object around(StaticJoinPoint sjp) throws Throwable { return sjp.proceed(); } @Around("execution(* foo.Bar.method(..))") Object around(JoinPoint jp) throws Throwable { return jp.proceed(); } @Around("call(* foo.Bar.method(..)) && args(i, j) && target(bar) && this(caller)") Object aroundCallWithBindings(int i, int j, Bar bar, Object caller, StaticJoinPoint sjp) throws Throwable { // no use of RTTI and tedious casting // much better performance bar.method(); int localInt = j + i; ... return sjp.proceed(); } // use of pointcut composition with argument bindings @Expression("execution(* foo.Bar.method(..)) && args(i)") void pointcut_1(int i) {} @Expression("execution(* foo.Bar.method(..)) && target(bar)") void pointcut_2(Bar bar) {} @Around("pointcut_1(myI) && pointcut_2(myBar)") Object around(JoinPoint jp, int myI, Bar myBar) throws Throwable { myBar.method(); return jp.proceed(); }
Since 2.0.RC3, StaticJoinPoint instances (and thus a JoinPoint instances) are exposing the
EnclosingStaticJoinPoint
instance through the API getEnclosingStaticJoinPoint
.
It can be usefull especially with call, get, set and handler joinpoints, to capture the enclosing static information
ie where the call/get/set/handler occurs.
The
Signature
interfaces are used for retrieving static information
about the join point we are currently executing at. The interfaces form a hierarchy in which you can
be both fine-grained and course-grained depending on you needs. Just cast to the appropriate interface.
The
Signature
can be retrieved from the
JoinPoint
or
StaticJoinPoint
by invoking
the
getSignature()
method.
The interfaces are:
org.codehaus.aspectwerkz.joinpoint.Signature
org.codehaus.aspectwerkz.joinpoint.MemberSignature
org.codehaus.aspectwerkz.joinpoint.CodeSignature
org.codehaus.aspectwerkz.joinpoint.MethodSignature
org.codehaus.aspectwerkz.joinpoint.ConstructorSignature
org.codehaus.aspectwerkz.joinpoint.FieldSignature
org.codehaus.aspectwerkz.joinpoint.CatchClauseSignature
The
Rtti
interfaces are used for retrieving RTTI (Runtime Type Information)
about the join point we are currently executing at. The interfaces form a hierarchy in which you can
be both fine-grained and course-grained depending on you needs. Just cast to the appropriate interface.
The
RTTI
can be retrieved from the
JoinPoint
by invoking
the
getRtti()
method.
The interfaces are:
org.codehaus.aspectwerkz.joinpoint.Rtti
org.codehaus.aspectwerkz.joinpoint.MemberRtti
org.codehaus.aspectwerkz.joinpoint.CodeRtti
org.codehaus.aspectwerkz.joinpoint.MethodRtti
org.codehaus.aspectwerkz.joinpoint.ConstructorRtti
org.codehaus.aspectwerkz.joinpoint.FieldRtti
org.codehaus.aspectwerkz.joinpoint.CatchClauseRtti
The
AspectContext
class provides methods for getting handles to various parts
of the system, that can be used for introspection as well as redefinitions of the runtime system.
To access the
AspectContext
in a specific aspect you have to define a
constructor that takes one
org.codehaus.aspectwerkz.AspectContext
as its only
parameter. The system will then take care of passing in a
AspectContext
instance
when the aspect is instantiated. This instance you can then use as you wish, store it in
a member field etc.
This class has many useful methods. For example:
... AspectContainer getContainer() void setParameter(String name, String value) String getParameter(String name) void setMetaData(Object key, Object value) Object getMetaData(Object key) int getDeploymentModel() ...
We have replaced the old mixin implementation with a completely new and better one. The new implementation mainly gives you two things you did not have before:
transient
(e.g. not serialized with
when the target class is serialized), by setting the
transient=true
flag.
org.codehaus.aspectwerkz.aspect.MixinFactory
interface.
The mixins can be any regular class, which means that it can be a standalone class, an inner class of a regular class or an inner class in your aspect class.
Three different
deployment models
are supported,
perJVM
,
perClass
and
perInstance
.
perJVM
- means that there will be one mixin per
JVM (like a singleton).
perClass
- means that there will be one mixin instance per
target class.
perInstance
- means that there will be one mixin instance per
target instance.
perThread
deployment model has been left to die in peace. The
perJVM
deployment model is currently not supported but might come back.
If you are using the default factory implementation you have to either provide a no argument constructor or a constructor that takes the:
perClass
deployment model.
perInstance
deployment model.
perJVM
.
To define the mixin using annotations you have to use the
org.codehaus.aspectwerkz.annotation.Mixin
annotation.
If using Java 5 this is an annotation interface (e.g.
@interface
) else
(for JavaDoc annotations) it is a regular interface.
This annotation has three different parameters:
expression
(can be used as the default anonymous value) - the pointcut expression that is used to pick out the set of classes
to apply this mixin to.
deploymentModel
(optional) - has to me one of
perJVM
,
perClass
or
perInstance
. If not specified then
perInstance
is used.
isTransient
(optional) - defines the mixin as transient or not. If not
specified then the default is
false
, e.g. serializable.
Definition using Java 5 annotations
@Mixin("within(*..domain.*)") public class MyMixin { ... } @Mixin( pointcut="within(*..domain.*)", deploymentModel="perClass", isTransient=true ) public class MyOtherMixin { ... }
Definition using JavaDoc annotations
/** @Mixin("within(*..domain.*)") */ public class MyMixin { ... } /** * @Mixin( * pointcut="within(*..domain.*)", * deploymentModel="perClass", * isTransient=true * ) */ public class MyOtherMixin { ... }
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 different 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
.
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.
Example:
<mixin class="foo.bar.MyMixinImpl"/> <mixin class="foo.bar.MyOtherMixinImpl" deployment-model="perClass" transient="true" factory="factories.MyMixinFactory" bind-to="within(*..domain.*)"/>
If you need to be in control of how the aspects are instantiated and/or need to pass in additional dependencies or configuration using an IoC (dependency injection) framework or similar. Then you can create custom mixin factory.
The custom factory needs to implement the
org.codehaus.aspectwerkz.aspect.MixinFactory
:
public interface MixinFactory { /** * Creates a new perClass mixin instance, if it already exists then return it. * * @param the target class * @return the mixin instance */ Object mixinOf(Class klass); /** * Creates a new perInstance mixin instance, if it already exists then return it. * * @param the target instance * @return the mixin instance */ Object mixinOf(Object instance); }
The custom mixin factory can only be defined in the
mixin
element in the
META-INF/aop.xml
file.
You can get a hold of the mixin instances for a specific instance or class through the
org.codehaus.aspectwerkz.aspect.management.Mixins
class.
Example:
// get per class deplolyed mixin Object perclassMixin = Mixins.mixinOf(MyMixin.class, Target.class); // get per instance deplolyed mixin Object perinstanceMixin = Mixins.mixinOf(MyMixin.class, targetInstance);
You can get a hold of specific aspect instances using the
org.codehaus.aspectwerkz.aspect.management.Aspects
class.
Example:
// get per jvm deployed aspect Object singletonAspect = Aspects.aspectOf(MyAspect.class); ... = Aspects.aspectOf("uniqueNameOfAspect"); // get per class deployed aspect Object perclassAspect = Aspects.aspectOf(MyAspect.class, Target.class); ... = Aspects.aspectOf("uniqueNameOfAspect", Target.class); // get per instance deployed aspect Object perinstanceAspect = Aspects.aspectOf(MyAspect.class, targetInstance); ... = Aspects.aspectOf("uniqueNameOfAspect", targetInstance);
This is a simple example showing how an aspect implementation with a couple of advice and an introduction could look like (Note: annotations to define the aspects are ommitted here):
public class MyAspect { public Object myAroundAdvice(StaticJoinPoint staticJoinPoint) throws Throwable { // do some stuff Object result = staticJoinPoint.proceed(); // do some other stuff return result; } public void myBeforeAdvice(JoinPoint joinPoint) { // do some stuff } // the Mixin could be an outer class public static class MyIntroduction extends SuperMixin implements ContractualInterface { ... // introduced methods and fields } }
The
XML definition allows to define the pointcut expressions in an external file.
It can be used
to provide aspects that needs to be tuned without an extra development phase (apart from editing an XML file).
The main disadvantage with this approach is that the implementation is separated from the definition which
makes the code harder to refactor, maintain and reuse.
Using the XML definition is a bit more difficult as well when pointcuts have complex signature to expose
runtime arguments and instances (args(..), this(..), target(..)).
The Annotation definition allows you to have truly self-contained aspect components. Those are easier to maintain, refactor and reuse as well as build libraries upon. The drawbacks are when you are not using Java 5, it requires an additional post-compilation step to annotate them with the metadata. Moreover it introduces tighter coupling, and makes the aspect harder to configure. You would have to extend the aspect to refine it.
We have exprerienced that a combination of both definition formats can be beneficial. For example define the reusable aspects and advice using annotations but do the pointcut definition in the external XML file. This allows you to make certain decisions later in the development cycle, for example at deployment time instead of at compile time.