Code Generation Library (CGLIB)
- is a byte instrumentation library used in many Java frameworks such as Hibernate or Spring which allows manipulating or creating classes after the compilation phase of a program
- the JDK dynamic proxy approach requires the target objects to implement one or more interfaces. What if you want to proxy legacy classes that do not have interfaces? You can use the CGLIB library
- uses ASM underneath
Dependencies
maven dependencies
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency>
Package Structure
|
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 |
Code Examples
Enhancer - creating proxies
A service class that you want to proxy
public class PersonService { public String sayHello(String name) { return "Hello " + name; } public Integer lengthOfName(String name) { return name.length(); } }Using Enhancer to proxy class
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.FixedValue; import net.sf.cglib.proxy.MethodInterceptor; public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) { return "Hello Tom!"; } else { return proxy.invokeSuper(obj, args); } }); PersonService proxy = (PersonService) enhancer.create(); String response1 = proxy.sayHello(null); int response2 = proxy.lengthOfName("Mary"); System.out.println(response1); System.out.println(response2); } }
BeanGenerator - creating beans/objects dynamically
BeanGeneratorallows dynamic creation of beans and to add fields together with setter and getter methods. It can be used by code generation tools to generate simple POJO objectsimport net.sf.cglib.beans.BeanGenerator; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class BeanGeneratorExample { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { BeanGenerator beanGenerator = new BeanGenerator(); // add property `name` (also adds methods: setName & getName) beanGenerator.addProperty("name", String.class); // create a bean object Object myBean = beanGenerator.create(); // set instance field `name` with some value via CGLIB Method setter = myBean.getClass().getMethod("setName", String.class); setter.invoke(myBean, "some string value set by a cglib"); // get value of instance field `name` via CGLIB Method getter = myBean.getClass().getMethod("getName"); String response = (String) getter.invoke(myBean); System.out.println(response); // outputs: "some string value set by a cglib" } }
Mixin - combining multiple objects together
A mixin is a construct that allows combining multiple objects into one. We can include the behavior of a couple of classes and expose that behavior as a single class or interface. The CGLIB Mixins allow the combination of several objects into a single object. However, in order to do so all objects that are included within a mixin must be backed by interfaces.
Let’s say that we want to create a mixin of two interfaces. We need to define both interfaces and their implementations:
public interface Interface1 { String first(); } public interface Interface2 { String second(); } public class Class1 implements Interface1 { @Override public String first() { return "first behaviour"; } } public class Class2 implements Interface2 { @Override public String second() { return "second behaviour"; } }To compose implementations of Interface1 and Interface2 we need to create an interface that extends both of them:
public interface MixinInterface extends Interface1, Interface2 { }By using a create() method from the Mixin class we can include behaviors of Class1 and Class2 into a MixinInterface:
Mixin mixin = Mixin.create( new Class[]{ Interface1.class, Interface2.class, MixinInterface.class }, new Object[]{ new Class1(), new Class2() } ); MixinInterface mixinDelegate = (MixinInterface) mixin; assertEquals("first behaviour", mixinDelegate.first()); assertEquals("second behaviour", mixinDelegate.second());