Konstantin Ignatyev e-mail Do not work harder, work smarter!
Home
Articles
Presentations
Projects
Personal
Annoyances

 

 

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.
Simple performance comparisons between various AOP systems and notes about those systems have moved here.

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

  • they do runtime byte modification and do not alter Java syntax( unlike AspectJ);

  • they do not require entire system to work within AOP ( like AspectWerkz );
  • do not require extra build step ( although Nanning support both offline code modification I did not try it yet );
  • They do not mess with classloaders ;

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:
- it is classloader safe;
- easy to debug (debugger steps into source);

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.
Basically, to add custom code before/after class methods we need to implement MethodInterceptor and do what we want there.

Here is CGLib MethodInterceptor that counts number of calls to the proxied class:


Of course functionality of the interceptor is far from being useful but allows to demonstrate concept of proxying. Much more useful example of dealing with database connection and exception handling will be described shortly.
Because AOP rumored to be very slow I decided to estimate its performance. To do that I have defined a very simple interface and supplied test method with different implementations or, to be more precise, different instances of the base class, which were altered by different frameworks.

Here is the interface:


Test class is very simple, it just takes different instances and calls a method the given numbers of times ( 1,000,000 in the case )



Result of the test is quite interesting:


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:

  • my method calls ThreadLocalEnv.getConnection() and expects that the method returns connection. The connection must be created by “before” interceptor code and returned to pool by “after” code. Using of ThreadLocal variables allows propagating of execution environment down to nested calls and executes them in the proper transaction context. Roughly speaking it does almost the same job as transaction-required attribute on EJB.

As we may see that code of the class is crystal clear and not polluted by any “plumbing” and transactional code:



Using of CGLib for generating proxy classes is surprisingly simple and elegant with only one public method

public synchronized static Object getEnhancedObjectOf( Class clazz).

It allows instrumenting all methods of any desired classes:



So usage of Instrumenter looks like the following snippet:

DBAccessor dba = ( DBAccessor ) DBConnectionInstrumenter.getEnhancedObjectOf( DBAccessor.class );
dba.find( "q");
dba.saveObject("o");

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.



Now the result of our test case:

>>>>>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