The article Java Dynamic Proxies: One Step from Aspect-oriented Programming has made me blog again (see here for my previous comments on AOP) on the subject of AOP.
AOP is a bitter pill to swallow for most quality engineers (both engineers of quality and quality assurance engineers), and I have spent many a frustrated moment persuading them that AOP is worth the risk. And I have no doubt that there are risks with using AOP.
First a bit of context. When used well AOP should be sweet as O'grady says and the metaphor man eats. But at the moment I side with James Gosling in that it has very few practical applications that make sense. Architects and designers have been using forms of "separation of concerns" for some time now, just consider the reasoning behind JavaBeans and MVC; what AOP does is take that a bit further (see IBM's Hyperspace) and the results should be marvelous. But for each type of concern you abstract with either AOP or Proxys you also veil those concerns making it harder to see what is going on. And if you hide something important then that is bad.
Proxy implementations are easier to set up and understand. They don't require a change to the build process or target environment, you just compile them and off they go. But this is also their drawback, you must be using a factory like pattern that allows the Proxy to be inserted, and this is a constraint (some might say a healthy one) on your software architecture. [Incidentally I try to use vdoclet to generate these factory relationships]
Programmers who understand Java reflection can also understand the call tree of Proxy usage. But this is also a performance hit as the passing of caller to proxy to real implementation is done usually using reflection which involves at least one hash lookup per call (although you could generate hard coded proxies, let me know if you do this). By contrast AOP cuts right in and hardly has any overhead above that of the aspect itself.
The Proxy implementations can themselves have Proxy based calls (as anyone who has found themselves in a toString fiasco will tell you) which is a mixed blessing. On the one hand you can treat your proxy code as any other code in the system and on the other you can easily recurse into an OutOfMemoryError. AOP typically is only second dimensional, but some Aspect of Aspect support exists.
The main difference is that proxy implementations live in the context of a contract that exists normally at the business level of an application (as most of the infrastructure of a software system is provided by classes rather than interfaces in Java); and it is this that I believe James was refering to as 'inappropriate'. The temptation to have business aspects is a wicked one.
So proxy implementations are more prescriptive, slower and inherently business orientated when compared to AOP in general. AOP compilers or classloaders provide all the tools you need to 'get it right', but they should still carry a health warning. So if you go dynamic then go AOP but be careful.
I think that time will show that AOP has a place in serious software engineering. I see that place as a step in a process rather than a component of a solution. In my mind the layering of software in multiple dimensions (true separation of concerns) requires AOP as a RAD tool, but also needs a process associated with it - perhaps using it to do early integration and prototyping. AOP has a role to play in the form of lightweight code generation, something that most developers try and stay away from, which has key benefits for software maintenance and its underlying cost. But all this takes guts and determination, which means projects have to try and sometimes fail.
One idea recently that might be worth exploring is to build AOP into the JVM and providing ready made hooks for the 'normal' use cases, but I digress.