AspectJ Parameter Binding

AspectJ parameter binding demystified

It’s easy to confuse AspectJ parameter binding with normal Java method parameter transfer. It confused me, anyway, until I realized there are some major differences:

  1. A pointcut is NOT a method call, therefore:
  2. The relevant parameters on a joinpoint have to be determined contextually, so
  3. The parameters are bound within the context of the joinpoint, not transferred.

A pointcut is NOT a method call

A pointcut is used to capture the occurrence of jointpoints in a program. A joinpoint is a ‘well defined point in the runtime execution of a program’. In other words, any point in your program where you actually do something (call a method, set a field, throw an exception etcetera) is a joinpoint.

So joinpoints shoud be seen along the lines of runtime activities and pointcuts help you filter out the activities you think you should do something with. There are more activities than merely calling methods, hence a pointcut is NOT a method call.

Relevant parameters on a joinpoint have to be determined contextually

If a pointcut is NOT a method call, then pointcuts do not actually have parameters transferred to them. This is logical: a joinpoint is ‘an activity’ which may not have much to do with parameter transfer at all.

However, there are paramaters which can be considered to be associated with a joinpoint. For example, for a method call or execution (indicated by a call or execution pointcuts), the associated parameters will likely be the transferred paramaters. When updating or reading a field (indicated by a set or get pointcuts), the associated parameter will be the field value.

The type of joinpoint determined which kinds of parameters are associated with that joinpoint. They are determined from the context of the jointpoint.

Parameters are bound within the context of the joinpoint

If the parameters which are associated with a joinpoint are determined contextually, how do we get access to those parameters? The answer is: we need to explicitly define that we want to have them available. We need to bind those parameters.

The difficulty with parameter binding in AspectJ is that it’s more or less an implicit mechanism. Associated parameters are automatically bound by contextual rules about the paramaters in the scope of a joinpoint and the type of the joinpoint. Binding is done via the name of the binding parameter, not its type, which is counterintuitive if you’re still thinking “method calls”.

The basic mechanism is this: you define the types and names in the pointcut definition, bind them to actual values through the pointcut expression, and then bind them yet again to the pointcut advice.

Example

With these three be-mindful-ofs in place, let’s look at an example aspect: 

public aspect BankingAspect {

 pointcut transferMoney() :  

  execution (* transfer(..))

  && args(banking.DepositAccount, banking.CheckingAccount,*);

 

 before() : transferMoney(){

 System.out.println("Called!");

 }

}

The pointcut matches all calls to transfer-like method executions with three parameters of type DepositAccount, CheckingAccount an one other of any type. “Before advice” is applied to this pointcut. (Note: The source code link is available at the end of this blog.)

 

Now, I consider the two accounts to be relevant parameters at the jointpoint. To get access to them, I need to do the following two things:

 

The pointcut will look like this: 

pointcut transferMoney

(banking.DepositAccount deposit, banking.CheckingAccount checking) :

 execution (* transfer(..)) && args(deposit, checking,*);

What I basically say is this: “I declare to have two objects in the jointpoint context, the first being of type DepositAccount with name deposit, the second of type CheckingAccount with name checking. Dear Runtime, please bind to ‘deposit’ the first object in the argument list of the method execution. Bind to ‘checking’ the second object in the argument list of the method excecution”.

Note that I do not bind the third argument in the argument list of the method execution.

 OK, so at any time where my pointcut matches the joinpoint according to the pointcut expression, I will have the two objects available. Now I only need to bind those pointcut-bound-objects to my advice:

before(banking.DepositAccount d,

 banking.CheckingAccount c)

 : transferMoney(d, c){

 System.out.println(d.getBalance());;

 System.out.println(c.getBalance());

}

Note that the binding names used are different from those in the pointcut. Also, the ordering in the advice (‘d’, ‘c’) is different from the ordering in the pointcut (‘c’, ‘d’). This is all entirely optional but I used it here to show how the binding works.

What I basically say here is this: “I declare to have two objects in the advice, the first being of type DepositAccount with name d, the second of type CheckingAccount with name c. Dear Runtime, please bind ‘d’ to the ‘d’-object in the pointcut. Bind ‘c’ to the ‘c’-object in the pointcut”.


The full aspect now looks like:

pointcut transferMoney


(banking.DepositAccount deposit,


banking.CheckingAccount checking) :


execution (* transfer(..)) && args(deposit, checking,*);



before(banking.DepositAccount d,


banking.CheckingAccount c) : transferMoney(d, c){


System.out.println(d.getBalance());;


System.out.println(c.getBalance());


}

Note that you need to declare all bound paramters in the advice, leaving one out will cause a compile error (assuming you use compile time weaving).

 So here’s the basic sequence of steps:

  1. Declare relevant parameters in pointcut;
  2. Bind to runtime arguments by name;
  3. Declare binding parameters in the advice;
  4. Bind pointcut parameters to the advice parameters by name;

Source Code for this blog.