CGLIB is a byte code generation library which creates and link proxy classes at runtime.As java classes can be dynamically linked , we can add new classes in running java program.It simply creates a subclass of your class by reading its byte code. Under the hood it uses ASM which is a byte code manipulation framework. ASM helps CGLIB to generate java byte code at runtime.
Frameworks like spring, hibernate, mockito uses CGLIB. Spring AOP uses proxy-based Aspect Oriented Programming which has a feature of method interception.Hibernate implemented a feature of returning proxy of an object of entity graph from database where child entities are fetched from database when required.Hibernate uses CGLIB for this feature.
Frameworks like spring, hibernate, mockito uses CGLIB. Spring AOP uses proxy-based Aspect Oriented Programming which has a feature of method interception.Hibernate implemented a feature of returning proxy of an object of entity graph from database where child entities are fetched from database when required.Hibernate uses CGLIB for this feature.
Lets start with some coding examples. CGLIB has a class named Enhancer which helps us to create proxy class for all the classes implementing or not implementing any interface. It is quite similar to Proxy class in java.
package com.akash.mycglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class FirstCGLIBProgram {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(DemoClass.class);
// here we have to specify object of a class that implements Callback interface
enhancer.setCallback(new MethodInterceptorProxy());
// Generate proxy class and its object and uses the no-arg constructor of the superclass
DemoClass proxy = (DemoClass) enhancer.create();
System.out.println(proxy.test1(null));
System.out.println("------------");
System.out.println(proxy.test2(null));
System.out.println("------------");
System.out.println(proxy.test3(null));
System.out.println("------------");
System.out.println(proxy.test5(null));
System.out.println("------------");
}
}
class MethodInterceptorProxy implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("intercepting method here...");
System.out.println("proxy :"+proxy.getClass().getName());
System.out.println("obj :"+obj.getClass().getName());
if (method.getDeclaringClass() != Object.class &&
method.getReturnType() == String.class) {
proxy.invokeSuper(obj, args);
return "Method Interceptor CGLIB";
} else {
return proxy.invokeSuper(obj, args);
}
}
}
class DemoClass {
//public methods are proxied
public String test1(String input) {
System.out.println("public method");
return "test-1";
}
//protected methods are proxied
protected String test2(String input) {
System.out.println("protected method");
return "test-2";
}
//default methods are proxied
String test3(String input) {
System.out.println("default method");
return "test-3";
}
//private methods are not proxied
private String test4(String input) {
System.out.println("private method");
return "test-4";
}
//static methods are not proxied
public static String test5(String input) {
System.out.println("static method");
return "test-5";
}
}
output:
intercepting method here...
proxy :net.sf.cglib.proxy.MethodProxy
obj :com.akash.mycglib.DemoClass$$EnhancerByCGLIB$$9c242b46
public method
Method Interceptor CGLIB
------------
intercepting method here...
proxy :net.sf.cglib.proxy.MethodProxy
obj :com.akash.mycglib.DemoClass$$EnhancerByCGLIB$$9c242b46
protected method
Method Interceptor CGLIB
------------
intercepting method here...
proxy :net.sf.cglib.proxy.MethodProxy
obj :com.akash.mycglib.DemoClass$$EnhancerByCGLIB$$9c242b46
default method
Method Interceptor CGLIB
------------
static method
test-5
------------
To understand above working, lets summarize some rules regarding CGLIB :
- private , static and final methods are not proxied.
- The class generated by cglib will be in same package as the proxied class and hence cglib can override default methods.
- final classes are not proxied.
- All classes generate by cglib are stored in a special section of memory called Metaspace.
- Only those methods are proxied that are invokeVirtual.
- invokeSpecial and invokeStatic methods are not proxied.
- static methods are invokeStatic.
- constructors , methods called using super keyword and private methods are invokeSpecial.
There are basically 3 types of methods at bytecode level in java
- invokespecial
- invokestatic
- invokevirtual
NOTE : Only invokevirtual methods are proxied by cglib.
you can determine these method types by reading byte code by using javap tool:
javap -c -private FirstCGLIBProgram.class
output :
Compiled from "FirstCGLIBProgram.java"
public class com.akash.mycglib.FirstCGLIBProgram {
public com.akash.mycglib.FirstCGLIBProgram();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: new #2 // class net/sf/cglib/proxy/Enhancer
3: dup
4: invokespecial #3 // Method net/sf/cglib/proxy/Enhancer."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // class com/akash/mycglib/DemoClass
11: invokevirtual #5 // Method net/sf/cglib/proxy/Enhancer.setSuperclass:(Ljava/lang/Class;)V
14: aload_1
15: new #6 // class com/akash/mycglib/MethodInterceptorProxy
18: dup
19: invokespecial #7 // Method com/akash/mycglib/MethodInterceptorProxy."<init>":()V
22: invokevirtual #8 // Method net/sf/cglib/proxy/Enhancer.setCallback:(Lnet/sf/cglib/proxy/Callback;)V
25: aload_1
26: invokevirtual #9 // Method net/sf/cglib/proxy/Enhancer.create:()Ljava/lang/Object;
29: checkcast #4 // class com/akash/mycglib/DemoClass
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: aconst_null
38: invokevirtual #11 // Method com/akash/mycglib/DemoClass.test1:(Ljava/lang/String;)Ljava/lang/String;
41: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
47: ldc #13 // String ------------
49: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
52: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
55: aload_2
56: aconst_null
57: invokevirtual #14 // Method com/akash/mycglib/DemoClass.test2:(Ljava/lang/String;)Ljava/lang/String;
60: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
63: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
66: ldc #13 // String ------------
68: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
71: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
74: aload_2
75: aconst_null
76: invokevirtual #15 // Method com/akash/mycglib/DemoClass.test3:(Ljava/lang/String;)Ljava/lang/String;
79: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
82: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
85: ldc #13 // String ------------
87: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
90: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
93: aload_2
94: pop
95: aconst_null
96: invokestatic #16 // Method com/akash/mycglib/DemoClass.test5:(Ljava/lang/String;)Ljava/lang/String;
99: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
102: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
105: ldc #13 // String ------------
107: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
110: return
}
NOTE : See lines 38,57,76,96 of the output from javap tool. This shows that test1() , test2() and test3() are proxied as they are invokevirtual. test5() method is not proxied as it is a invokestatic method.
There are few more child interfaces of Callback interface that we should know while using CGLIB :
- MethodInterceptor : All generated proxied methods call this method instead of the original method.The original method may either be invoked by normal reflection using the Method object,or by using the MethodProxy (faster).
- NoOp : Methods using this callback will delegate directly to the default (super) implementation in the base class.
- LazyLoader : Called as soon as the first lazily-loaded method in the enhanced instance is invoked. The same object is then used for every future method call to the proxy instance.
- Dispatcher : This is similar to LazyLoader but it always evaluates the returning value.
- InvocationHandler : This callback type is primarily for use by the Proxy class but may be used with Enhancer as well.
- FixedValue : callback that simply returns the value to return from the proxied method. No information about what method is being called is available to the callback, and the type of the returned object must be compatible with the return type of the proxied method.
In the end we must be aware of Object class methods while creating proxies.As final methods are not proxied, Object#wait() Object#notify() and Object#notifyAll() are not proxied. Object#finalize() and Object#clone() should never be intercepted while creating a proxy class. Garbage collector handles proxied Object#finalize() method differently.Also if any hard reference are taken in proxied method , then it may result in some serious problems in case of Object#finalize() method.
Thanks
java proxy fundamentals, well explained
ReplyDeleteAll proxy concepts in java in one blog, well done
ReplyDeleteHarrah's Cherokee Casino Resort debuts COVID-19 vaccination
ReplyDeleteHarrah's Cherokee Casino 충청남도 출장샵 Resort has rolled out COVID-19 vaccination 순천 출장샵 options 광주 출장마사지 into 통영 출장마사지 its casino 오산 출장안마 floors this week. The hotel's COVID-19 vaccination