View Full Version : AOP General Approach
Tom Whitner
10-20-2004, 02:29 PM
I was curious if you could describe the general approach that Spring.NET is taking for implementing AOP. I am marginally familiar with attibute based approaches, code weaving, modifying the IL at runtime, etc. How are you skinning the "AOP cat"?
Thanks,
Tom
choyrim
10-22-2004, 03:53 AM
We create proxy types using Reflection.Emit that implement the proxied interfaces. This approach allows us to handle interception and introduction separately. So introductions don't have to go through interceptors.
Currently we only proxy interfaces. We do not implement overrides of virtual methods. But that may change in the future.
Since we create dynamic assemblies, and types, we also have the associated visibility issues. For example, we wouldn't be able to proxy any class that is not visible outside the declaring assembly.
As mentioned before on another response, Aleksander Seovic implemented the majority of the AOP module. It is no mean feat so it bears repeating the credits.
--Choy
Aleks Seovic
10-23-2004, 05:19 AM
Current implementation uses composition to delegate method calls to appropriate object, either target or introduction, after executing interceptors.
Each proxy class extends BaseCompositionProxy that holds references to TargetSource and an array of configured introductions, among other things. Generated IL code for proxy methods executes interceptor chain (if any interceptors are configured for the method) and delegates call either to target object or appropriate introduction, based on interface mappings.
Code that is currently in CVS proxies all possible interface members (methods, properties and events) and handles reference types, value types and enums properly, both as arguments and as return values.
We also tried to make members in the proxy class as similar to target members as possible -- all the flags are copied from respective target members during IL generation. One thing that we were not able to replicate so far are .Net attributes defined on the target members, which might be a problem in certain usage scenarios.
There are few things that I like about current approach:
1. It allowed us to make introductions "equal citizens" -- calls to introduced methods and to target methods have equal cost because final target for the call is resolved during proxy generation. Also, you can intercept calls to introduced methods the same way you would intercept calls to target methods.
2. There is virtually no performance penalty for calls to methods without interceptors -- these calls are "hardcoded" during IL generation using target object or introduction directly, which completely avoids reflection and its performance impact. This means that you can introduce functionality into existing objects and pay very little, if anything, in performance.
4. It wraps target as IDisposable and calls Dispose method at the end of each proxy method that delegates call to target object. In most cases, such as for SingletonTargetSource, call to Dispose doesn't do anything, but it comes very handy when using PooledTargetSource where it returns target object to the pool automatically.
One caveat is that we can only proxy interfaces, as Choy already mentioned. We considered inheritance-based implementation as well, but decided to implement composition-based approach first, after Ted Neward brought up a good point that unlike in Java, methods in .Net have to be explicitly defined as overrideable in order for inheritance-based proxy to work.
Hope this provides a decent overview of current AOP implementation. We'll try to implement missing pieces and get all the tests to run in the next few weeks so we can do 0.7 release which will include fully functional AOP in addition to IoC container.
Regards,
Aleks
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.