For classes without interfaces, a dynamic cglib-based proxy can be used instead of a final method.
The bottom level of cglib is to use the byte code processing framework asm to manipulate the byte code generation proxy class at the assembly instruction level of the underlying JVM.
Use cases:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
public class CglibTest { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PlayGame.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method run..."); Object result = proxy.invokeSuper(obj, args); System.out.println("after method run..."); return result; } }); PlayGame sample = (PlayGame) enhancer.create(); sample.say("hahha"); } }
Like Proxy, the above code also generates another Class file: CglibTest .class, which implements the MethodInterceptor interface.
principle
Actually, it is similar to Proxy in principle, but different from Proxy proxy class which inherits Proxy, the proxy class generated with cglib inherits the proxy class and implements the Factory interface.
We can preface the code with System. SetProperty (DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,'D:/'); The proxy class can be generated at the specified location. It generates three class files:
From top to bottom is 1. fastclass 2 of the proxy class. Proxy Class 3. fastclass of proxied class
View the decompiled code for the proxy class:
public class PlayGame$$EnhancerByCGLIB$$74aa30cd extends PlayGame implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$say$0$Method; private static final MethodProxy CGLIB$say$0$Proxy; ............ //Bottom static code block calls this method initialization static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.baiye.proxy.PlayGame$$EnhancerByCGLIB$$74aa30cd"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$finalize$1$Method = var10000[0]; CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1"); CGLIB$equals$2$Method = var10000[1]; CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); ........ CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.baiye.proxy.PlayGame")).getDeclaredMethods())[0]; CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "say", "CGLIB$say$0"); } final void CGLIB$say$0(String var1) { super.say(var1); } public final void say(String var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { //Call Interceptor var10000.intercept(this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy); } else { super.say(var1); } } } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch(var10000.hashCode()) { case -1853413644: if (var10000.equals("say(Ljava/lang/String;)V")) { return CGLIB$say$0$Proxy; } break; case -1574182249: if (var10000.equals("finalize()V")) { return CGLIB$finalize$1$Proxy; } break; case -508378822: if (var10000.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$5$Proxy; } break; case 1826985398: if (var10000.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$2$Proxy; } break; case 1913648695: if (var10000.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$3$Proxy; } break; case 1984935277: if (var10000.equals("hashCode()I")) { return CGLIB$hashCode$4$Proxy; } } return null; } public PlayGame$$EnhancerByCGLIB$$74aa30cd() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { PlayGame$$EnhancerByCGLIB$$74aa30cd var1 = (PlayGame$$EnhancerByCGLIB$$74aa30cd)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null) { return; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } .... public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); PlayGame$$EnhancerByCGLIB$$74aa30cd var10000 = new PlayGame$$EnhancerByCGLIB$$74aa30cd; switch(var1.length) { case 0: var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } } ..... public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default: } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; } static { CGLIB$STATICHOOK1(); }
The proxy class gets all methods inherited from the parent class, where private MethodInterceptor CGLIB$CALLBACK_0; Property is the key, the proxy class holds it and then calls var10000.intercept(this, CGLIB$say var10000. Intercept (this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);$Method, new Object[]{var1}, CGLIB$say var10000. Intercept (this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);$Proxy); Determine the method from the fastclass file.
Class decompilation of the proxied class:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.baiye.proxy; import java.lang.reflect.InvocationTargetException; import net.sf.cglib.core.Signature; import net.sf.cglib.reflect.FastClass; public class PlayGame$$FastClassByCGLIB$$5f92a78b extends FastClass { public PlayGame$$FastClassByCGLIB$$5f92a78b(Class var1) { super(var1); } public int getIndex(Signature var1) { String var10000 = var1.toString(); switch(var10000.hashCode()) { case -1853413644: if (var10000.equals("say(Ljava/lang/String;)V")) { return 0; } break; . . . . . . case 1984935277: if (var10000.equals("hashCode()I")) { return 6; } } return -1; } public int getIndex(String var1, Class[] var2) { switch(var1.hashCode()) { case -1776922004: if (var1.equals("toString")) { switch(var2.length) { case 0: return 5; } } break; . . . . . case 113643: if (var1.equals("say")) { switch(var2.length) { case 1: if (var2[0].getName().equals("java.lang.String")) { return 0; } } } break; . . . . . . case 1950568386: if (var1.equals("getClass")) { switch(var2.length) { case 0: return 7; } } } return -1; } public int getIndex(Class[] var1) { switch(var1.length) { case 0: return 0; default: return -1; } } public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { PlayGame var10000 = (PlayGame)var2; int var10001 = var1; try { switch(var10001) { case 0: var10000.say((String)var3[0]); return null; case 1: var10000.wait(); return null; . . . . . . case 9: var10000.notifyAll(); return null; } } catch (Throwable var4) { throw new InvocationTargetException(var4); } throw new IllegalArgumentException("Cannot find matching method/constructor"); } public Object newInstance(int var1, Object[] var2) throws InvocationTargetException { PlayGame var10000 = new PlayGame; PlayGame var10001 = var10000; int var10002 = var1; try { switch(var10002) { case 0: var10001.<init>(); return var10000; } } catch (Throwable var3) { throw new InvocationTargetException(var3); } throw new IllegalArgumentException("Cannot find matching method/constructor"); } public int getMaxIndex() { return 9; } }
The class decompilation of the proxy class is almost too much code to show.
By looking at the other two FastClass files, you can see that we can match the method we want and call through the method index without requiring reflection like the JDK dynamic proxy, which greatly improves the efficiency of execution. It simply works by generating a Class for each of the proxy and proxy classes, which assigns an index(int type) to the method of the proxy or proxy class.
As a parameter, FastClass can directly locate the method to be invoked to make a direct call, eliminating the reflection call, so it is more efficient to invoke than the JDK dynamic proxy through reflection calls.
Differences between JDK dynamic proxy and Gglib dynamic proxy:
1. The JDK dynamic proxy implements the interface of the proxy object, and Cglib inherits the proxy object.
2. Both JDK and Cglib generate byte codes at runtime. JDK writes Class byte codes directly. Cglib writes Class byte codes using ASM framework. Cglib proxy implementations are more complex and the generation of proxy classes is less efficient than JDK.
3.JDK calls proxy methods through reflection mechanism, Cglib calls methods directly through FastClass mechanism, and Cglib executes more efficiently.
Reference resources: https://www.cnblogs.com/monkey0307/p/8328821.html