Different Java proxy based frameworks with pros and cons.

Today there are several tools that can be used to manipulate bytecode, ranging from very low-level tools such as ASM, which require you to work at the bytecode level, to high level frameworks such as CGlib, AspectJ, which allow you to write pure Java. Ofcourse each framework have there own limitations in creating proxies.

For the moment, I just want to provide a quick summary on the pros and cons of proxy frameworks available. Hope everyone knows about the GOFProxy design pattern.

JDK dynamic proxies:

The JDK dynamic Proxy relies heavily on interface based implementations.JDK comes with the class java.lang.reflect.Proxy that allows you to create a dynamic proxy for a given interface. The InvocationHandler that sits behind the dynamically created class is called every time the application invokes a method on the proxy. Hence you can control dynamically what code is executed before the code of some framework or library is called.

Dynamic proxies can be used for many different purposes, e.g. database connection and transaction management, dynamic mock objects for unit testing, and other AOP-like method intercepting purposes.

Hibernate for lazy loading entities, Spring for AOP, LambdaJ for DSL, only to name a few: they all use their hidden magic. What are they? They are… Java’s dynamic proxies.

Limitation of JDK Dynamic proxy can only proxy by interface (so your target class needs to implement an interface).Any interface method is then forwarded to an InvocationHandler.If class does not implemented from interface then you will see ClassCastException.

Sample code to create proxy using JDK proxies.


MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[]{MyInterface.class},
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("your method name")) {
                            // do want ever magic you want...
                        }
                    }
                });
The invoke method will intercept all method calls on your proxied object.

CGLib proxies :

Java proxies are runtime implementations of interfaces. Objects do not necessarily implement interfaces, so Java proxies fail to provide an answer for a class not implemented from interface.

That's where CGlib came out.CGlib is a third-party framework, based on bytecode manipulation provided by ASM that can help with the previous limitations. 


     1. The proxies are created by sub-classing the actual class. This means wherever an instance of the class is used it is also possible to use the CGLib proxy.
     2. The class needs to provide a default constructor, i.e. without any arguments. Otherwise,you'll get an IllegalArgumentException: "Superclass has no null constructors but no arguments were given." This makes constructor injection impossible.
     3. The proxying does not work with final methods since the proxy subclass can not override the class' implementation.
     4. The CGLib proxy is final, so proxying a proxy does not work. You will get an IllegalArgumentException.
     5. Two objects are created (the instance of the class and the proxy as instance of a sub class) the constructor is called twice.
     6. Documentation of CGlib is not complete.
     7. The proxies are not Serializable.
     8. You will need add CGLIB binaries on your classpath or dependencies.
     9. Furthermore, cglib can improve performance by specialized interceptions like FixedValue,NoOp,LazyLoader...
CGlib introduced many callback handlers for better performance.Please go with my previous post "Creating Proxies Dynamically Using CGLIB Library with examples".

Spring AOP: CGLIB or JDK Dynamic Proxies ?

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice). If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created. 

If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so.



ASM :

CGLIB and almost all other libraries are built on top of ASM which itself acts on a very low level. This is a show-stopper for most people as you have to understand the byte code and a little bit of the JVMS to use it properly. But mastering ASM is most certainly very interesting. Note however that while there is a great ASM 4 guide, in some part of the API the javadoc documentation can be very concise if it is present at all, but it is being improved. It closely follows JVM versions to support new features.

Javassist :

The javadoc of Javassist is way better than that of CGLIB. The class engineering API is OK, but Javassist is not perfect either. In particular, the ProxyFactory which is the equivalent of the CGLIB's Enhancer suffer from some drawbacks too, just to list a few :

      1. Bridge method are not fully supported (ie the one that are generated for covariant return types).
      2. ClassloaderProvider is a static field instead, then it applies to all instances within the same classloader.
      3. Custom naming could have been welcome (with checks for signed jars).
      4. There is no extension point and almost all methods of interest are private, which is cumbersome if we want to change some behavior.
      5. While Javassist offer support for annotation attributes in classes, they are not supported in ProxyFactory. 

On the aspect oriented side, one can inject code in a proxy, but this approach in Javassist is limited and a bit error-prone .

      1. aspect code is written in a plain Java String that is compiled in opcodes.
      2. no type check.
      3. no generic.
      4. no lambda.
      5. no auto-(un)boxing

Also Javassist is recognized to be slower than Cglib. This is mainly due to its approach of reading class files instead of reading loaded classes such as CGLIB does. And the implementation itself is hard to read to be fair ; if one requires to make changes in the Javassist code there's many chances to break something.

Byte Buddy :

Byte Buddy is a rather new library but provides any functionality that CGLIB provides. Byte Buddy can be fully customized down to the byte code level and comes with an expressive domain specific language that allows for very readable code.

It supports all JVM bytecode versions, including Java 8 semantic changes of some opcodes regarding default methods.

      1. ByteBuddy don't seem to suffer from the drawbacks other libraries have.
     2. Highly configurable.
     3. Quite fast (benchmark code).
     4. Type safe.
     5. Type safe callbacks.
    6. Very well documented.
    7. Lots of example.
    8. Annotation driven (flexible).
    9. Available as an agent.
    10. Clean code, 93% test coverage

You can find more details on the benchmark in Byte Buddy's tutorial, where Byte Buddy is a more modern alternative to cglib. Also, note that cglib is no longer under active development.

Here are some metrics for implementing an interface with 18 stub methods:
Byte Buddy        CGlib                 Javassist         JDK proxy         
Creation 0.234 0.613 0.391 0.309
invocation       0.002   0.019  0.027 0.003 

The time is noted in nanoseconds.

In fact there are other proxy frameworks like BCEL, AspectJ, JiteScript, Proxetta are not discussed in this post as there are not much familiar.

To summarize, out of all frameworks Byte Buddy is a most modern(as of 2015) proxy framework with precise documentation and with various examples.Mockito used CGlib over years,in recent time mockito is replacing CGLIB by Byte Buddy.



Show Comments: OR

0 comments: