1

<img src="https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/20210925233820.png" width = "500" height = "400" alt="picture name" align=center />

cglib dynamic proxy

Introduction to cglib

CGLIB is an open source project, a powerful, high-performance and high-quality code generation library that can expand Java classes and implement Java interfaces during runtime. The bottom layer is to use a small and fast bytecode processing framework ASM to convert bytecode and generate new classes.

In theory, we can also directly use ASM to directly generate code, but we are required to be familiar with the JVM internals, the class file format, and the bytecode instruction set.

not in the JDK package, you need to download and import it yourself or import it with Maven coordinates.

I choose Maven import and add it to the pom.xml file:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Student.java:

public class Student {
    public void learn() {
        System.out.println("我是学生,我想学习");
    }
}

MyProxy.java (agent class)


import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class StudentProxy implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("代理前 -------");
        proxy.invokeSuper(obj, args);
        System.out.println("代理后 -------");
        return null;
    }

}

Test class ( Test.java )


import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/aphysia/Desktop");

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new StudentProxy());
        Student student = (Student) enhancer.create();
        student.learn();

    }
}

The result after running is:

CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
代理前 -------
我是学生,我想学习
代理后 -------

In the folder we selected, the code of the proxy class is generated:

Source code analysis

The class we need to proxy first needs to implement the MethodInterceptor (method interceptor) interface, this interface has only one method intercept , the parameters are:

  • obj: the object that needs to be enhanced
  • method: the method that needs to be intercepted
  • args: method parameters to be intercepted
  • proxy: means to trigger the method object of the parent class
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

enhancer.create() that we want to create a proxy class. The meaning of this method: If necessary, generate a new class, and use the specified callback (if any) to create a new object instance. Use the parameterless constructor of the superclass.

    public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();
    }

The main method logic we have to look at createHelper() , except for verification, is to call the KEY_FACTORY.newInstance() method to generate the EnhancerKey object, KEY_FACTORY is the static EnhancerKey interface, newInstance() is a method in the interface, the focus is on the method of the parent class super.create(key)

    private Object createHelper() {
        // 校验
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

AbstractClassGenerator is Enhancer parent class, create(key) main logic method is to obtain a class loader, class cache obtaining load data, and then construct an object reflector, there are two ways to create instances of objects:

  • fistInstance() : This method should not be called in a regular stream. Technically speaking, {@link #wrapCachedClass(Class)} uses {@link EnhancerFactoryData} as the cache value, which supports faster instantiation than ordinary old reflection lookups and calls. For backward compatibility reasons, this method remains the same: just in case it has ever been used. (My understanding is that the current logic will not go to this branch, because it is relatively busy, but for compatibility, this case is still saved), the practical internal logic is ReflectUtils.newInstance(type) .
  • nextInstance() : The real class for creating proxy objects
    protected Object create(Object key) {
        try {
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            if (data == null) {
                synchronized (AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            // 真正创建对象的方法
            return nextInstance(obj);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

This method is defined in AbstractClassGenerator , but it is actually Enhancer , mainly by obtaining parameter types, parameters, and callback objects, and using these parameters to reflect and generate proxy objects.

    protected Object nextInstance(Object instance) {
        EnhancerFactoryData data = (EnhancerFactoryData) instance;

        if (classOnly) {
            return data.generatedClass;
        }

        Class[] argumentTypes = this.argumentTypes;
        Object[] arguments = this.arguments;
        if (argumentTypes == null) {
            argumentTypes = Constants.EMPTY_CLASS_ARRAY;
            arguments = null;
        }
        // 构造
        return data.newInstance(argumentTypes, arguments, callbacks);
    }

The internal implementation logic is ReflectUtils.newInstance() , and the parameter types are different:

        public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
            setThreadCallbacks(callbacks);
            try {

                if (primaryConstructorArgTypes == argumentTypes ||
                        Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
                    return ReflectUtils.newInstance(primaryConstructor, arguments);
                }
               return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
            } finally {
            
                setThreadCallbacks(null);
            }

        }

To follow up to the end is to obtain the constructor method, construct the proxy object in reflection, and finally call the method provided by the JDK:

    public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
        return newInstance(getConstructor(type, parameterTypes), args);
    }
    public static Object newInstance(final Constructor cstruct, final Object[] args) {
            
        boolean flag = cstruct.isAccessible();
        try {
            if (!flag) {
                cstruct.setAccessible(true);
            }
            Object result = cstruct.newInstance(args);
            return result;
        } catch (InstantiationException e) {
            throw new CodeGenerationException(e);
        } catch (IllegalAccessException e) {
            throw new CodeGenerationException(e);
        } catch (InvocationTargetException e) {
            throw new CodeGenerationException(e.getTargetException());
        } finally {
            if (!flag) {
                cstruct.setAccessible(flag);
            }
        }
                
    }

Open the proxy class file that it automatically generates and you will find that those methods are actually generated, plus some enhancement methods:

The generated proxy class inherits the original class:

public class Student$$EnhancerByCGLIB$$929cb5fe extends Student implements Factory {
    ...
}

Looking at the generated enhancement method, it actually calls the intercept() method. This method is implemented by ourselves in front of us, so the enhanced function of the proxy object is completed:

    public final void learn() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
        } else {
            super.learn();
        }
    }

What is the difference between cglib and jdk dynamic proxy

  1. The jdk dynamic proxy is an anonymous class that uses interceptor and reflection to generate a proxy interface, which is handed over to InvokeHandler for processing when the method is executed. The CGLIB dynamic proxy uses the ASM framework, modifies the original bytecode, and then generates a new subclass for processing.
  2. JDK agents need to implement interfaces, but CGLIB is not mandatory.
  3. Before JDK1.6, cglib used bytecode generation technology, which was more efficient than reflection. However, jdk has also been optimized afterwards, and the efficiency has been improved.

[Profile of the author] :
Qin Huai, [1618fbde4ae15e Qinhuai Grocery Store ], the road to technology is not at a time, the mountains are high and the rivers are long, even if it is slow, it will never stop. Personal writing direction: Java source code analysis, JDBC , Mybatis , Spring , redis , distributed, sword refers to Offer, LeetCode , do not like to write each article in the series of fancy articles. , I cannot guarantee that what I have written is completely correct, but I guarantee that what I have written has been practiced or searched for information. I hope to correct any omissions or errors.

refers to all offer solutions PDF

What did I write in 2020?

Open source programming notes


秦怀杂货店
144 声望35 粉丝

山高水长,纵使缓慢,驰而不息。