Few Thoughts on Aspects (v. CDI Interceptors)
I have to admit, when AOP first made the scene, I thought it would have limited usefulness. Then I started using it for things and liked it a lot. But as is so often the case in Java, the poor state of the tools made me feel that the core technology was great, but it was perhaps not worth the hassle. The AspectJ Plugin for eclipse is a major undertaking, but it was very buggy and then they decided during Helios to not support any of the milestone builds until halfway through the project.
Well, AOP is popping its head back in the tent because CDI deems to give us interceptors that look a lot like AOP. Though, they are missing one of the most fundamental elements: pointcuts. If you look around at the criticism of AOP, 90% of it concerns the pointcut syntax, which, for whatever reasons, has made people crazy. It's not the easiest thing in the world, but it's also not that hard. The problem with not having any pointcut capability is that you are then required to have compile-time designations as to where the crosscutting incisions will occur. Kind of defeats the purpose. The whole idea is that the aspect provides a mechanism for weaving in orthogonal concerns. The great part about a pointcut is it provides a means of describing all such cases in advance and in the future.
Without pointcuts, aspects are merely interceptors, and they have to be ones that don‘t require any special knowledge of what they are interrupting. One of the things I used aspects for before was to do type-based persistence where images could have their contents put into a share (e.g. S3 or a webdav folder) and the location of that information could be updated. So, in other words, you have a wizard where you let me sign up for something, and part of that is uploading a picture. You can just keep that image in your object graph and when the attempt is made to persist it, I look for images in the graph being persisted and if I find them, I do the above-mentioned switcheroo.
What‘s good about an example like this is it accomplishes one of the most important goals of reuse: provide additional functionality that can be leveraged into the future without incurring what we like to call ‘debt‘ these days: some cost to maintain, move, extend, learn, debug, etc. The pointcut syntax in this case was pretty simple: I just intercept create and update calls on repositories. Future users need not worry about how their images get moved to the share, where the credentials are, etc.
With the new interception model, you could argue a tiny debt would be paid: you would have to know about this thing, then you would have to annotate the places where the interceptions would occur, so all future repositories that used this mechanism would need to add this functionality. The part I don‘t like about this solution is that it‘s a case of an object (Image) not being able to manage its own affairs because it is at the wrong layer of abstraction, and the default persistence layer does not address this case. Another approach would be to treat the remote file store as a different data source and do a second repository then add chaining logic.
There are two articles out there on CDI‘s interceptors. One is Rick Hightower‘s (here). This one is by far the most complete. The only quibble I have with this one is he talks about thinking of interceptors as decorators, but in fact, CDI has decorators, and there is a difference: decorators are to be used when the intercepting party does have domain-specific knowledge: the decorator is augmenting, meaning it is providing domain-oriented (rather than orthogonal) additions. I think the addition of Decorator is great in CDI. It‘s not wrong to say aspects can be used to make Decorators, but generally, you would only do Decorators with traditional AOP when you didn‘t have the code you were weaving into. The CDI Decorator is a totally different case though because it‘s providing a means of wrapping the bean container injection services around the decoration.
The other way to look at interceptors is as a way to keep the code cleaner and more readable. From that perspective, it is a very good idea. There is also one other advantage of doing interception: it ensures that people will go along with standard approaches most of the time. We had a case recently where we were importing data from a feed and someone had added some explicit transaction logic that was attempting to flush a transaction after a failure.
I was looking at interceptors again for a piece that involves using Reflection. Interestingly, I am going to support indicating participants both through annotations and events. I was hoping to do it without the need of compile-time indicators. One of the really interesting things about the whole idea of interception is that it encourages you to think about the separated concerns independently, then focus on how to reconnect them in the least disruptive way possible. That‘s the design principle behind the Jacobson book.