Aspects

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:

  • a default no-argument constructor - only needed if the aspect has a other constructors that takes parameters (apart from the one below if defined)


  • a constructor that takes a org.codehaus.aspectwerkz.CrossCuttingInfo instance as its only parameter - this is needed if you want to retrieve information about the runtime system, access paramaters defined at deployment time, access meta-data etc. See Using the CrossCuttingInfo class section for details.


If a constructor of type 2 is found then it is used else the default constructor. If no default constructor can be 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.

Abstract aspects - aspect inheritance

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.

Advice

In the aspect class the advice are regular methods. The methods needs to conform to a specfic signature unless args() selector is used in the pointcut the advice is bounded to.

  • For Around advice:

    public Object <name of method>(JoinPoint joinPoint) throws Throwable signature.


  • For Before and After advice:

    public void <name of method>(JoinPoint joinPoint) throws Throwable signature.
The advice can then be defined using XML or annotations, see the XML definition section or the Annotation definition section for more information.

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).

public Object myAroundAdvice(JoinPoint joinPoint) throws Throwable {
    // do some stuff
    Object result = joinPoint.proceed();
    // do some more stuff
    return result;
}
            

If a pointcut is using the args() selector to gain direct access to the advised method/constructor/field set the Pointcut declaration itself will have a 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.

Such an advice can be defined thru Annotations and thru XML. See the XML definition section or the Annotation definition section for more information.

The advice signature is thus depending on the pointcut signature, although the JoinPoint argument is mandatory. 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, JoinPoint joinPoint) {... joinPoint.proceed(); ...}

@After myPointcut(adviceArg)
public void myAfterAdvice(JoinPoint joinPoint, String adviceArg) {...}
                

JoinPoint instance

Each advice is passed a JoinPoint instance. The JoinPoint class implements the join point concept, i.e. a well-defined point in the program flow.

The JoinPoint instance contains static info and RTTI (runtime type information) about the join point we are currently executing over. The static info (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().

The JoinPoint clas has a method called proceed(), this method is used in Around advice to either:

  • Invoke the next advice in the chain.


  • Invoke the target join point (if there are no more advice).
The 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.

Warning: if the proceed() method is invoked within an Before or After advice it will currently result in infinite recursion.

The JoinPoint class has some other interesting methods as well, see the JavaDoc for details.

Signature interfaces

The Signature interfaces are used for retrieving static about the join point we are currently executing at. The interfaces forms 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 ber retrieved from the JoinPoint 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
See the JavaDoc for API details.

RTTI interfaces

The Rtti interfaces are used for retrieving RTTI (Runtime Type Information) about the join point we are currently executing at. The interfaces forms 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 ber retrieved from the JoinPoint by invoking the getSignature() 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
See the JavaDoc for API details.

Using the CrossCuttingInfo class

The CrossCuttingInfo 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 CrossCuttingInfo in a specific aspect or mixin you have define a constructor that takes one org.codehaus.aspectwerkz.CrossCuttingInfo as its only parameter. The system will then take care of passing in a CrossCuttingInfo instance when the aspect of mixin 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:

...
RuntimeSystem getSystem()
AspectContainer getContainer()

void setParameter(String name, String value)
String getParameter(String name)

void setMetaData(Object key, Object value)
Object getMetaData(Object key)

int getDeploymentModel()
void setDeploymentModel(int deploymentModel)

Object getMixinTargetInstance(String mixinName, Object mixinImpl)
Class getMixinTargetClass(String mixinName, Object mixinImpl)
...
                

Introductions

Introductions makes it possible to extend a class with a new interface and/or a new implementation (methods and fields). AspectWerkz supports two different types of introductions:

  • Interface introductions - in which you introduce an interface to the target class.


  • Implementation introductions - where you introduce implementations to a target class.

Implementation introductions (interface with concrete implementation), in AspectWerkz implemented as Mixins, are defined as public static inner classes of the aspect class or as regular POJOs outside the aspect class.

It is mandatory for the inner class to be public, apart from this 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:

  • a default no-argument constructor - only needed if the aspect has a other constructors that takes parameters (apart from the one below if defined)


  • a constructor that takes a org.codehaus.aspectwerkz.CrossCuttingInfo instance as its only parameter - this is needed if you want to retrieve information about the runtime system, access paramaters defined at deployment time, access meta-data etc. See Using the CrossCuttingInfo class section for details.


If a constructor of type 2 is found then it is used else the default constructor. If no default constructor can be found then an exception is thrown.

The inner class implements the interface(s) to introduce on the target class.

Example of an implementation introduction that will introduce the implemetatation of the inner class along with the interfaces the inner class implements to the target classes defined in the definition.

public MyAspect {

    public static class MyIntroduction extends SuperIntroduction implements ToBeIntroduced {
        // methods to be introduced
        ...
    }
}
                

Implementation swapping

It is possible to replace the introduction implementation at runtime using a call like

SystemLoader.getSystem("systemId").getAspectManager().getMixin("test.attribdef.aspect.IntroductionTestAspect$MyImpl").
    swapImplementation("test.attribdef.aspect.IntroductionTestAspectMyImplReplacement");
                    

The swapped implementation must implement the introduced interfaces, but can be an anonymous class or an aspect inner class. Only the default introduced implementation must be an inner class.

Example of an Aspect

This is a simple example showing how an aspect implementation with a couple of advice and an introduction could look like:

public class MyAspect {

     public Object myAroundAdvice(JoinPoint joinPoint) throws Throwable {
         // do some stuff
         Object result = joinPoint.proceed();
         // do some other stuff
         return result;
     }

     public void myBeforeAdvice(JoinPoint joinPoint) throws Throwable {
         // do some stuff
     }

    public static class MyIntroduction extends SuperMixin implements ContractualInterface {
        ... // introduced methods and fields
    }
}
                    

Choosing a definition model

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.

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 that it requires an additional post-compilation step (until Java 1.5) to annotate them with the metadata and introduces tighter coupling, makes the aspect harder to configure etc.

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.