Create Proxies Dynamically Using CGLIB Library
By Jason Zhicheng Li, OCI Senior Software Engineer
November 2005
Introduction to CGLIB Library
A proxy provides a surrogate or place holder for the target object to control access to it. It introduces a level of indirection when accessing an object. The JDK dynamic proxy, which has been available since JDK 1.3, is often used to create proxies dynamically. The JDK dynamic proxy is simple to use, but the JDK dynamic proxy approach requires the target objects implement one or more interfaces. What if you want to proxy legacy classes that do not have interfaces? You can use the CGLIB library.
CGLIB is a powerful, high performance code generation library. It is widely used behind the scenes in proxy-based Aspect Oriented Programming (AOP) frameworks, such as Spring AOP and dynaop, to provide method interceptions. Hibernate, the most popular object-relational mapping tool, also uses the CGLIB library to proxy single-ended (many-to-one and one-to-one) associations (not lazy fetching for collections, which is implemented using a different mechanism). EasyMock and jMock are libraries for testing Java code using mock objects. Both of them use the CGLIB library to create mock objects for classes that do not have interfaces.
Under the covers, the CGLIB library uses ASM, a small but fast bytecode manipulation framework, to transform existing bytecode and generates new classes. In addition to the CGLIB library, scripting languages, such as Groovy and BeanShell, also use ASM to generate Java bytecode. ASM uses a SAX parser like mechanism to achieve high performance. Using ASM directly is not encouraged because it requires good knowledge of the JVM, including class file format and the instruction set.
Figure 1 shows the relationship among the CGLIB library related frameworks and languages. Note that some frameworks, such as Spring AOP and Hibernate, often use both the CGLIB library and the JDK dynamic proxy to meet their needs. Hibernate uses the JDK dynamic proxy to implement a transaction manager adapter for the WebShere application server; Spring AOP, by default, uses the JDK dynamic proxy to proxy interfaces unless you force the use of the CGLIB proxy.
CGLIB Proxy APIs
The CGLIB library code base is small, but it is difficult to learn due to lack of documentation. The current version (2.1.2) of the CGLIB library is organized as follows:
net.sf.cglib.core
- Low-level bytecode manipulation classes; Most of them are related to ASM.
net.sf.cglib.transform
- Classes for class file transformations at runtime or build time
net.sf.cglib.proxy
- Classes for proxy creation and method interceptions
net.sf.cglib.reflect
- Classes for a faster reflection and C#-style delegates
net.sf.cglib.util
- Collection sorting utilities
net.sf.cglib.beans
- JavaBean related utilities
To create proxies dynamically, most of the time, you only need to deal with a few APIs in the proxy package.
As discussed in preceding section, the CGLIB library is a high-level layer on top of ASM. It is very useful for proxying classes that do not implement interfaces. Essentially, it dynamically generates a subclass to override the non-final methods of the proxied class and wires up hooks that call back to user-defined interceptors. It is faster than the JDK dynamic proxy approach.
CGLIB library APIs commonly used for proxying concrete classes are illustrated in Figure 2. The net.sf.cglib.proxy.Callback
interface is a marker interface. All callback interfaces used by the net.sf.cglib.proxy.Enhancer
class extend this interface.
The net.sf.cglib.proxy.MethodInterceptor
is the most general callback type. It is often used in proxy-based AOP implementations to intercept method invocations. This interface has a single method:
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;
When net.sf.cglib.proxy.MethodInterceptor
is the callback for all methods of a proxy, method invocations on the proxy are routed to this method before invoking the methods on the original object. It is illustrated in Figure 3. The first argument is the proxy object. The second and third arguments are the method being intercepted and the method arguments, respectively. The original method may either be invoked by normal reflection using the java.lang.reflect.Method
object or by using the net.sf.cglib.proxy.MethodProxy
object. net.sf.cglib.proxy.MethodProxy
is usually preferred because it is faster. In this method, custom code can be injected before or after invoking the original methods.
net.sf.cglib.proxy.MethodInterceptor
meets any interception needs, but it may be overkill for some situations. For simplicity and performance, additional specialized callback types are offered out of the box. For examples,
net.sf.cglib.proxy.FixedValue
- It is useful to force a particular method to return a fixed value for performance reasons.
net.sf.cglib.proxy.NoOp
- It delegates method invocations directly to the default implementations in the super class.
net.sf.cglib.proxy.LazyLoader
- It is useful when the real object needs to be lazily loaded. Once the real object is loaded, it is used for every future method call to the proxy instance.
net.sf.cglib.proxy.Dispatcher
- It has the same signatures as
net.sf.cglib.proxy.LazyLoader
, but theloadObject
method is always called when a proxy method is invoked. net.sf.cglib.proxy.ProxyRefDispatcher
- It is the same as
Dispatcher
, but it allows the proxy object to be passed in as an argument of theloadObject
method.
A callback is often used for all methods in the proxy class, as shown in Figure 3, but you can use net.sf.cglib.proxy.CallbackFilter
to selectively apply callbacks on the methods. This fine-grained control feature is not available in the JDK dynamic proxy, where the invoke
method of java.lang.reflect.InvocationHandler
applies to all the methods of the proxied object.
In addition to proxying classes, CGLIB can proxy interfaces by providing a drop-in replacement for java.lang.reflect.Proxy to support Java proxying prior to JDK 1.3. Since this replacement proxy capability is rarely used, the related proxy APIs are not covered here.
The proxy package also provides support for net.sf.cglib.proxy.Mixin
. Basically, it allows multiple objects to be combined into a single larger object. The method invocations on the proxy are delegated to the underlying objects.
Let's see how to create proxies using CGLIB proxy APIs.
Create a Simple Proxy
The core of the CGLIB proxying is the net.sf.cglib.proxy.Enhancer
class. To create a CGLIB proxy, at the minimum, you need a class. Let's use the built-in NoOp
callback first:
- /**
- * Create a proxy using NoOp callback. The target class
- * must have a default zero-argument constructor.
- *
- * @param targetClass the super class of the proxy
- * @return a new proxy for a target class instance
- */
- public Object createProxy(Class targetClass) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(targetClass);
- enhancer.setCallback(NoOp.INSTANCE);
- return enhancer.create();
- }
The return value is a proxy for an instance of the target class. In this example, a single net.sf.cglib.proxy.Callback
is configured for the net.sf.cglib.proxy.Enhancer
class. It can be seen it is fairly straightforward to create a simple proxy. Instead of creating a new instance of net.sf.cglib.proxy.Enhancer
, you can simply use the static helper methods in the net.sf.cglib.proxy.Enhancer
class to create proxies. It is preferred to use the approach shown in the above example because it allows you to configure the net.sf.cglib.proxy.Enhancer
instance to fine control the generated proxies.
Note that the target class is passed in as the super class of the generated proxy. Unlike the JDK dynamic proxy, you cannot pass in the target object during the proxy creation. The target object must be created by the CGLIB library. In this example, the default zero-argument constructor is used to create the target instance. If you want the CGLIB to create an instance with some arguments, instead of net.sf.cglib.proxy.Enhancer.create(),
the net.sf.cglib.proxy.Enhancer.create(Class[], Object[])
method should be used. The first argument specifies argument types and second argument values. Primitive types are wrapped in the arguments.
Use a MethodInterceptor
To make the proxy more useful, you can replace the net.sf.cglib.proxy.NoOp
callback with a custom net.sf.cglib.proxy.MethodInterceptor
. All the method invocations on the proxy are dispatched to the single intercept
method of net.sf.cglib.proxy.MethodInterceptor
. The intercept
method then delegates the invocations to the underlying object.
Assume you want to apply authorization check for all the method calls of the target object. If authorization fails, a runtime exception, AuthorizationException
, will be thrown. The Authorization.java
interface is listed below:
- package com.lizjason.cglibproxy;
-
- import java.lang.reflect.Method;
-
- /**
- * A simple authorization service for illustration purpose.
- *
- * @author Jason Zhicheng Li (jason@lizjason.com)
- */
- public interface AuthorizationService {
- /**
- * Authorization check for a method call. An AuthorizationException
- * will be thrown if the check fails.
- */
- void authorize(Method method);
- }
The implementation of net.sf.cglib.proxy.MethodInterceptor
is as follows:
- package com.lizjason.cglibproxy.impl;
-
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import com.lizjason.cglibproxy.AuthorizationService;
-
- /**
- * A simple MethodInterceptor implementation to
- * apply authorization checks for proxy method calls.
- *
- * @author Jason Zhicheng Li (jason@lizjason.com)
- *
- */
- public class AuthorizationInterceptor implements MethodInterceptor {
- private AuthorizationService authorizationService;
-
- /**
- * Create a AuthorizationInterceptor with the given
- * AuthorizationService
- */
- public AuthorizationInterceptor (AuthorizationService authorizationService) {
- this.authorizationService = authorizationService;
- }
-
- /**
- * Intercept the proxy method invocations to inject authorization check.
- * The original method is invoked through MethodProxy.
- * @param object the proxy object
- * @param method intercepted Method
- * @param args arguments of the method
- * @param proxy the proxy used to invoke the original method
- * @throws Throwable any exception may be thrown; if so, super method will not be invoked
- * @return any value compatible with the signature of the proxied method.
- */
- public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {
- if (authorizationService != null) {
- //may throw an AuthorizationException if authorization failed
- authorizationService.authorize(method);
- }
- return methodProxy.invokeSuper(object, args);
- }
- }
In the intercept
method, the authorization is checked first. If authorization passes, then the intercept
method invokes the original method on the target object. For performance reasons, the original method is invoked by using the CGLIB net.sf.cglib.proxy.MethodProxy
object instead of normal reflection using the java.lang.reflect.Method
object.
Use a CallbackFilter
A net.sf.cglib.proxy.CallbackFilter
allows you to set callbacks at the method level. Assume you have a PersistenceServiceImpl
class, which has two methods: save
and load
. The save
method requires an authorization check, but the load
method does not.
- package com.lizjason.cglibproxy.impl;
-
- import com.lizjason.cglibproxy.PersistenceService;
-
- /**
- * A simple implementation of PersistenceService interface
- *
- * @author Jason Zhicheng Li (jason@lizjason.com)
- */
- public class PersistenceServiceImpl implements PersistenceService {
-
- public void save(long id, String data) {
- System.out.println(data + " has been saved successfully.");
- }
-
- public String load(long id) {
- return "Jason Zhicheng Li";
- }
- }
Note that PersistenceServiceImpl
class implements PersistenceService
interface, but this is not required to generate proxies using CGLIB. The net.sf.cglib.proxy.callbackfilter
implementation for PersistenceServiceImpl
is as follows:
- package com.lizjason.cglibproxy.impl;
-
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.CallbackFilter;
-
- /**
- * An implementation of CallbackFilter for PersistenceServiceImpl
- *
- * @author Jason Zhicheng Li (jason@lizjason.com)
- */
- public class PersistenceServiceCallbackFilter implements CallbackFilter {
-
- //callback index for save method
- private static final int SAVE = 0;
-
- //callback index for load method
- private static final int LOAD = 1;
-
- /**
- * Specify which callback to use for the method being invoked.
- * @method the method being invoked.
- * @return the callback index in the callback array for this method
- */
- public int accept(Method method) {
- String name = method.getName();
- if ("save".equals(name)) {
- return SAVE;
- }
- // for other methods, including the load method, use the
- // second callback
- return LOAD;
- }
- }
The accept
method maps proxy methods to callbacks. The return value is the index in the callback array for the particular method. Here is the proxy creation implementation for the PersistenceServiceImpl
class:
- ...
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(PersistenceServiceImpl.class);
-
- CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
- enhancer.setCallbackFilter(callbackFilter);
-
- AuthorizationService authorizationService = ...
- Callback saveCallback = new AuthorizationInterceptor(authorizationService);
- Callback loadCallback = NoOp.INSTANCE;
- Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
- enhancer.setCallbacks(callbacks);
- ...
- return (PersistenceServiceImpl)enhancer.create();
In this example, the AuthorizationInterceptor
instance applies to the save
method and the NoOp.INSTANCE
to the load
method. Optionally, you can specify the interfaces that the proxy object is to implement through the net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])
method.
In addition to setting an array of callbacks to the net.sf.cglib.proxy.Enhancer
, you can specify an array of callback types through net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[])
method. The callback types are useful when you do not have an array of actual callback instances during proxy creation. Like callbacks, you need to use net.sf.cglib.proxy.CallbackFilter
to specify the callback type index for each method. You can download complete source code from http://www.lizjason.com/downloads/ for examples of setting callback types and interfaces.
Summary
CGLIB is a powerful, high performance code generation library. It is complementary to the JDK dynamic proxy in that it provides proxying classes that do not implement interfaces. Under the covers, it uses ASM bytecode manipulation framework. Essentially, CGLIB dynamically generates a subclass to override the non-final methods of the proxied class. It is faster than the JDK dynamic proxy approach, which uses Java reflection. CGLIB cannot proxy a final class or a class with any final methods. For general cases, you use the JDK dynamic proxy approach to create proxies. When the interfaces are not available or the performance is an issue, CGLIB is a good alternative.
References
- [1] Complete source code for this article
http://www.lizjason.com/downloads/ - [2] CGLIB library
http://cglib.sourceforge.net - [3] Spring Framework
http://www.springframework.org - [4] JDK dynamic proxy
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/Proxy.html - [5] EasyMock
http://www.easymock.org - [6] jMock
http://www.jmock.org - [7] dynaop
http://dynaop.dev.java.net - [8] A good introduction to ASM by Eugene Kuleshov
http://www.onjava.com/lpt/a/5250
Jason Zhicheng Li would like to thank Paul Jensen and Tom Wheeler for reviewing this article.
Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.