« What the teacher said (using AspectJ with Spring part II) | Main | Sun reveal what they really think of AOP »

May 20, 2004

Effective AspectJ - Understanding type checking for pointcuts and advice

I'll get back onto the Spring theme very soon, but thought I'd mix things up a little today and touch on a different topic.

An AspectJ user raised a bug this morning about AspectJ's type checking, which turned out to be 'working as designed' (*). The rules that AspectJ follows seem completely natural to me now, but I remember a time when the treatment of formal parameters in advice and pointcuts wasn't quite so obvious. Perhaps you could argue that I'm too close to the code, but I think the more significant shift was that I changed the words I say in my head when I read a pointcut definition. Now it's a struggle for me to remember how I could ever think it was any other way. So, the aim of today's post is to help you understand how pointcut and advice parameters work, by looking at a couple of code examples.

Consider the following pointcut and advice definitions:

pointcut accountOperation(Account acc) : execution(public * *(..)) && this(acc); after(CurrentAccount cAcc) returning : accountOperation(acc) { ... }

Assuming that CurrentAccount is a subclass of Account then this should be fine, right? The after advice takes a CurrentAccount, the pointcut needs an Account, and CurrentAccount is an Account.

But it's not fine at all - if you try to compile this code you'll get an incompatible type error. I've led you up the garden path by using misleading language to describe the situation. The thing is, that unlike method parameters (or advice parameters) which are passed into the method/advice body, pointcut parameters go the other way. Pointcut parameters are not a definition of what the pointcut needs to be passed to it, but instead a declaration of the context information that a pointcut provides at a join point it matches. Although pointcut parameters is a correct term for them (in the sense that they parameterize the pointcut), I stopped calling the formals in a pointcut declaration the parameter list (because to my mind at least that has a connotation of values being passed in), and started to call them instead the 'provides' list. Ever since I made that switch, I've found the type matching rules completely natural and intuitive.

Here's how to read the definitions given above using our new terminology: the accountOperation pointcut provides an object of type Account at each join point it matches, and the after advice requires an object of type CurrentAccount. Since the object provided by the pointcut could be any type of Account, and not just a CurrentAccount, this is a type error as the advice is not guaranteed to get an instance of the type it needs.

In contrast, the example below is perfectly well-formed:

pointcut currentAccountOperation(CurrentAccount acc) : execution(public * *(..)) && this(acc); after(Account acc) : currentAccountOperation(acc) { ... }

The currentAccountOperation pointcut provides a value of type CurrentAccount, and the after advice requires an object of type Account. Since a CurrentAccount is an Account everything works just fine.

* The actual bug report was in a slightly different area, and had to do with the return type of around advice not being compatible with the expected return value from a call join point:

double around() : call(int foo()) { return (proceed() * Math.PI); }

Can you see the subtle type error here?

Posted by adrian at May 20, 2004 04:10 PM [permalink]


hi adrian, thats way cool to not get confused. a 'eselsbrucke' (donkeys bridge) germans would say. ciao robertj

Posted by: robertj at May 27, 2004 07:33 AM

So what you are saying is that the after advice specifying an Account instance is actually unnecesssary because it will never be an instance of anything other than CurrentAccount?

Wouldn't the following:

pointcut accountOperation() : execution(public Account+.*(..)) && this(acc);

after(CurrentAccount cAcc) returning : accountOperation(acc) {

be ok?

Posted by: Michael M at July 19, 2004 10:06 PM

(I tried the previous, but it didn't work, just the way you stated.)

I guess the main point of your lesson is that just because the syntax resembles Java, DOES NOT MEAN that it's working the same way.

Hmmm. Really interesting example of knowledge representation overloading. I guess the best thing would have been is to use a completely different syntax to represent the idea of context information providers. Oh well.

Posted by: Michael M at July 19, 2004 11:25 PM

Post a comment

Thanks for signing in, . Now you can comment. (sign out)

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Remember me?