|
|||||
|
Pragmatic byte code manipulation. Published 1-Jan-2004 - last edition::10-Dec-2004 Summary: Adopting some parts of
AOP ( Aspect Oriented Programming) helps reaching better and cleaner
code. Proxy based aspectization may help get started with AOP and gradually migrate to some full-blown AOP system as you get more familiar with strengths and weaknesses of AOP. Sources: Download. I kept my eyes on AOP for awhile but did not try to use it out of usual caution. I read articles but they did not convince me so far that AOP is worth using, especially because frameworks like AspectJ alter Java syntax. It prevented me from even looking at AspectJ because it breaks my beloved code completion and other conveniences of my favorite IDE. In the same time I knew that my another favorite – Hibernate ORM uses runtime code manipulation and does not disturb my normal development process at all. So, finally I decided to play a little bit with Runtime Code manipulation. As usual I started with some kind of HelloWorld type but decided to extend it a bit to estimate performance implications of AOP. Even I tend to trade convenience and clarity for performance I would like to test influence of AOP framework myself because I have read that AOP causes very serious performance degradation. There are several AOP frameworks available but I decided to try couple of them: Nanning and cglib, because
Both frameworks are “proxy”
based. It means that they create a proxy classes which implement base
class API and add custom pieces of code before/after execution of
base class method and/or on instance creation. There was long discussion on TSS about Proxy based AOP vs. develoment time byte code weaving vs custom classloaders. I expressed idea that Proxy based approach is more friendly because: After the discussion I decided to include AspectWerkz in the evaluation ( 13-Sep-04) and have discovered that debugging is not that hard, it is still possible to go step-by-step in sources, debugger get lost only for a moment when it steps into uncharted territory of generated bytecode. Nanning is more
sophisticated than CGLIb and allows defining pointcuts and other AOP
goodies, but for simple proxy class generation they are roughly the
same and provide very similar API. Here is CGLib MethodInterceptor that counts number of calls to the proxied class:
Here is the interface:
As we may see that using of Nanning AOP slowed down execution 140 times and that CGLib is much less expensive (only 6 times slower) but still causes significant performance decrease. ( More about performance) The numbers make me believe that it is to early to allow entire system be instrumented by AOP frameworks but in the same time convenience of creating before/after interceptors convinced me that AOP frameworks worth using in some parts of a system to get rid of repetitive “plumbing” code: something like typical try-catch-finally handler for dealing with JDBC connections. NOTE: Most AOP tutorials emphasize that AOP is transparent and can be applied to any class. Even that is possible I think it is wrong approach: I am convinced that classes should be developed with full knowledge and reliance on that they are going to be instrumented. Another tip: I have impression that AOP vendors and evangelists are trying to sell AOP as a novelty approach and hide that AOP in its core nothing more than good old C/C++ macro on steroids. That is it: AOP simply allows code insertion at selected places. In case of macro we had to type macro at exact place where we want to insert certain code snipped, AOP allows specifying template that causes chosen code snippets (proudly named Aspects now) to be inserted at specified places. That is it folks. I will focus on CGLib because Hibernate's success creates precedent of its successful use and makes me believe that abilities of CGLib as proxy-based AOP should enough in many real life scenarios. Now lets create a test case that will do something more realistic and useful when leveraging runtime proxies. I decided that my class will expect that external AOP interceptor will create appropriate environment for code execution and will do necessary cleanup. This approach seems working best in conjunction with using ThreadLocal variables. General idea:
As we may see that code of the class is crystal clear and not polluted by any “plumbing” and transactional code:
public synchronized static Object getEnhancedObjectOf( Class clazz). It allows instrumenting all methods of any desired classes:
DBAccessor dba = (
DBAccessor )
DBConnectionInstrumenter.getEnhancedObjectOf( DBAccessor.class
); Contrary to clear business logic code in the DBAccessor class our Method interceptor heavily uses low level database access code and ThreadLocal variables. In the absence of proxy we will need to have copy of the code in every(!) method of our DAOAccessor class that will obscure our business logic and make code more error prone. This is where AOP shines: as development goes on and we create more and more methods in DBAccessor class, we do not have to worry about plumbing code, it gets applied automatically during runtime enhancement of our class.
>>>>>Open connection DBAccessor.find <<<<<Close connection, submit transaction >>>>>Open connection DBAccessor.saveObject <<<<<Close connection, submit transaction >>>>>Open connection DBAccessor.findAndSave DBAccessor.find DBAccessor.saveObject <<<<<Close connection, submit transaction >>>>>Open connection DBAccessor.findAndSave DBAccessor.find DBAccessor.saveObjectWithException <<<<<Exception:: Close connection, rollback transaction There was an exception in some nested methods. e = java.lang.Exception: testException >>>>>Open connection DBAccessor.findAndSaveWithDeepException DBAccessor.findAndSave DBAccessor.find DBAccessor.saveObjectWithException <<<<<Exception:: Close connection, rollback transaction There was an exception in some nested methods. e = java.lang.Exception: testException Process finished with exit code 0 As we may see interceptor propagates necessary runtime environment down to nested method calls and successfully deals with exceptions in the nested methods. IMO: This example clearly demonstrates that limited adoption of certain AOP features leads to better and cleaner code. Personally I still cautious about AOP, but definitely encourage using runtime proxies for production systems and experimenting with AOP in spare time. Happy coding, Konstantin - 01-Jan-2004 PS:since writing of the article I have succesfully use CGLib proxied DAO objects for a quite large production system and very happy about that.( 20-Dec-2004) PS2: Experiments with various AOP systems lead to writing of a little performance comparison test.
|
||||
| © 2001 - 2006 Konstantin Ignatyev | |||||