Environment: Java 8
Cglib agent usage
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib"); Enhancer enhancer = new Enhancer() ; enhancer.setSuperclass(PersonDAOImpl.class) ; enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before execution...") ; Object result = proxy.invokeSuper(obj, args) ; System.out.println("After execution...") ; return result ; } }); PersonDAOImpl dao = (PersonDAOImpl) enhancer.create() ; dao.save(new Person()) ;
System.setProperty(
DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib"); This line of code is used to generate the proxy class generated by cglib and output it to the specified directory
Decompile proxy class
The tool is Luyten
Due to the large amount of code, only important codes are posted here
public class PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 extends PersonDAOImpl implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$save$0$Method; private static final MethodProxy CGLIB$save$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7"); final Class<?> forName2; final Method[] methods = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$1$Method = methods[0]; CGLIB$equals$1$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = methods[1]; CGLIB$toString$2$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = methods[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = methods[3]; CGLIB$clone$4$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); final Class<?> forName3; CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0]; CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0"); } final void CGLIB$save$0(final Person person) { super.save(person); } public final void save(final Person person) { MethodInterceptor cglib$CALLBACK_2; MethodInterceptor cglib$CALLBACK_0; if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) { CGLIB$BIND_CALLBACKS(this); cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0); } if (cglib$CALLBACK_0 != null) { cglib$CALLBACK_2.intercept((Object)this, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy); return; } super.save(person); } public PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] array) { PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.set(array); } private static final void CGLIB$BIND_CALLBACKS(final Object o) { final PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 personDAOImpl$$EnhancerByCGLIB$$6f07c3f7 = (PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7)o; if (!personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND) { personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND = true; Object o2; if ((o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$STATIC_CALLBACKS) != null) { personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0]; } } } static { CGLIB$STATICHOOK1(); } }
The generated proxy class inherits PersonDAOImpl; Several methods in PersonDAOImpl will generate two corresponding methods. One is to directly call the parent object method CGLIB$save cglib $save $0,, and the other is the save method, which is called through Callback
When executing the save method, first judge cglib $callback_ Whether 0 is empty. If it is empty, the initialization method will be executed
CGLIB$BIND_CALLBACKS(this);
In this method, from cglib $thread_ Get the Callback method (Callback) from the callbacks object (ThreadLocal). How does the ThreadLocal object set the value?
Set CGLIB$THREAD_CALLBACKS value
Executing enhancer The cglib $set of the proxy class PersonDAOImpl$$EnhancerByCGLIB$f07c3f7 is called by reflection when the create() method is used_ THREAD_ Callbacks method
When creating a proxy class:
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) { setThreadCallbacks(callbacks); try { // Explicit reference equality is added here just in case Arrays.equals does not have one if (primaryConstructorArgTypes == argumentTypes || Arrays.equals(primaryConstructorArgTypes, argumentTypes)) { // If we have relevant Constructor instance at hand, just call it // This skips "get constructors" machinery return ReflectUtils.newInstance(primaryConstructor, arguments); } // Take a slow path if observing unexpected argument types return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments); } finally { // clear thread callbacks to allow them to be gc'd setThreadCallbacks(null); } }
setThreadCallbacks(callbacks); This line of code is used to execute cglib $set in the proxy class_ THREAD_ Callbacks method.
private void setThreadCallbacks(Callback[] callbacks) { try { setThreadCallbacks.invoke(generatedClass, (Object) callbacks); } catch (IllegalAccessException e) { throw new CodeGenerationException(e); } catch (InvocationTargetException e) { throw new CodeGenerationException(e.getTargetException()); } }
The MethodInterceptor in this agent class is set. Next, the following code of the save method in the agent class will be executed:
cglib$CALLBACK_2.intercept((Object)this, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy);
Execution of intercept method in MethodInterceptor
intercept method
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before execution...") ; Object result = proxy.invokeSuper(obj, args) ; System.out.println("After execution...") ; return result ; }
Where does the MethodProxy proxy object come from? This needs to go back to the following line of code in the static code segment in the proxy class:
CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
Enter methodproxy Create method:
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) { MethodProxy proxy = new MethodProxy(); proxy.sig1 = new Signature(name1, desc); proxy.sig2 = new Signature(name2, desc); proxy.createInfo = new CreateInfo(c1, c2); return proxy; }
Enter the invokeSuper method
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } }
The init method is used to initialize a fastClassInfo object
private void init() { if (fastClassInfo == null) { synchronized (initLock) { if (fastClassInfo == null) { CreateInfo ci = createInfo; FastClassInfo fci = new FastClassInfo(); fci.f1 = helper(ci, ci.c1); fci.f2 = helper(ci, ci.c2); fci.i1 = fci.f1.getIndex(sig1); fci.i2 = fci.f2.getIndex(sig2); fastClassInfo = fci; createInfo = null; } } } } // FastClassInfo private static class FastClassInfo { FastClass f1; FastClass f2; int i1; int i2; }
f1 corresponds to c1 in CreateInfo, f2 corresponds to c2 in CreateInfo; Here, we will combine the static code snippets in the proxy class to see who is executing respectively
final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7"); final Class<?> forName3; CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0]; CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
Combined with the above code is very clear.
f1 represents the fast access class of the target class and f2 represents the fast access class of the proxy class; i1 represents the index corresponding to the target class method, and i2 represents the index corresponding to the proxy class method.
The quick access class inherits the FastClass class class. Its purpose is to call and execute quickly, because the reflection performance is not high.
The principle is to generate an index value for each method by signing the information (method name, parameter) of each method, and then calling the target method directly according to the index value when calling the method.
Quick access class of target class
Continue back to invokeSuper method execution
FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args);
At this time, the complete information of fci object is as follows:
fci.f2.invoke(fci.i2, obj, args); This line of code is the code that actually executes the target class. Note that i2=19 here
Go to persondaoimpl $$enhancerbycglib $$6f07c3f7 $$fastclassbycglib $$8343b7a5 Class to view the invoke method
CGLIB$saveCglib $save $0 method in PersonDAOImpl$$EnhancerByCGLIB$f07c3f7 class
Directly call the save method in the parent class PersonDAOImpl.
Here you can understand the three class es generated by cglib.
complete!!!
Give me a follow + forward, thank you
Public: Springboot combat case collection
Spring Cloud link tracking zipkin and integrated Elasticsearch storage
Springboot integrates the MyBatis parameter value transfer method
Springboot integrates MyBatis complex query application
Spring cloud Nacos service consumers
Implementation of Springboot interface idempotency based on token
The Springboot project is deployed using docker
How many methods can you use to verify interface parameters in Springboot?
SpringBoot is an operation to improve N-fold performance
Spring boot integrates Quartz to realize task scheduling
Detailed explanation of Actuator of Springboot
SpringCloud zuul dynamic gateway configuration
Spring MVC embedded Tomcat zero configuration
Spring MVC request principle and source code analysis
Spring MVC custom annotations implement interface calls
Detailed steps of integrating ELK log collection with Springboot