编程知识 cdmana.com

Design pattern [3.3] - Interpretation of cglib dynamic agent source code

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

cglib A dynamic proxy

cglib Introduce

CGLIB It's an open source project , A powerful high-performance and high-quality code generation library , It can be expanded during operation Java class , Realization Java Interface, etc. . 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 use ASM To directly generate code , But ask us to JVM Inside ,class File format , And the instruction set of bytecode .

This thing is not here JDK Inside the bag , You need to download, import or Maven Coordinate import .

I choose Maven Import , Add to pom.xml file :

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


     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

Student.java:

public class Student {
    public void learn() {
        System.out.println(" I am a student , I want to learn. ");
    }
}

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

MyProxy.java( proxy 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(" Before the agency  -------");
        proxy.invokeSuper(obj, args);
        System.out.println(" After agency  -------");
        return null;
    }

}


     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

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) {
        //  proxy class class The file is saved to the local disk for us to decompile and view the source code 
        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();

    }
}


     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

The result of running is :

CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
 Before the agency  -------
 I am a student , I want to learn. 
 After agency  -------

     
  • 1.
  • 2.
  • 3.
  • 4.

In the folder we chose , Generated the code of the proxy class :

 Design patterns 【3.3】-- CGLIB Dynamic agent source code interpretation _ proxy class

Source code analysis

Let's first proxy the class , Need to achieve MethodInterceptor( method interceptors ) Interface , This interface has only one method intercept, The parameters are :

  • obj: Objects that need to be enhanced
  • method: The way to intercept
  • args: Method parameters to be intercepted
  • proxy: Represents the method object to trigger 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;
}

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Let's go back to the method we want to create the proxy class enhancer.create(), The meaning of this method : if necessary , Generate a new class , And use the specified callback ( If any ) To create a new object instance . Parameterless constructor using superclass .

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

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

The main method logic we have to look at createHelper(), Besides checking , It's called KEY_FACTORY.newInstance() Method generation EnhancerKey object ,KEY_FACTORY Is static EnhancerKey Interface ,newInstance() Is a method in the interface , A focus on super.create(key) Inside , It's called the method of the parent class :

    private Object createHelper() {
        //  check 
        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;
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

AbstractClassGenerator yes Enhancer Parent class of ,create(key) The main logic of the method is to get the class loader , Cache get class load data , Then reflect the construction object , There are two methods to create instance objects :

  • fistInstance(): This method should not be invoked in the regular stream. . Technically speaking ,{@link #wrapCachedClass(Class)} Use {@link EnhancerFactoryData} As cache value , The latter supports faster instantiation than normal old reflection lookups and calls . For backward compatibility reasons , This method remains unchanged : Just in case it's ever been used .( My understanding is that the current logic will not go to this branch , Because it's busy , But for compatibility , This case Still remain in the ), Internal logic actually uses ReflectUtils.newInstance(type).
  • nextInstance(): Real class to create proxy object
    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);
            }
            //  The way to actually create an object 
            return nextInstance(obj);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

This method is defined in AbstractClassGenerator, But it's actually calling subclasses Enhancer The implementation of the , Mainly by obtaining parameter types , Parameters , And callback objects , Use these parameters to 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;
        }
        //  structure 
        return data.newInstance(argumentTypes, arguments, callbacks);
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Internal implementation logic , All the calls are ReflectUtils.newInstance(), 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);
            }

        }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

Follow in to the end , Is to get the constructor method , Construct proxy objects by reflection , The final call is JDK Methods provided :

    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);
            }
        }
                
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

Open the automatically generated proxy class file and have a look , You will find that it is actually generating those methods , Some enhancement methods are added :

 Design patterns 【3.3】-- CGLIB Dynamic agent source code interpretation _ A dynamic proxy _02

The generated proxy class inherits the original class :

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

     
  • 1.
  • 2.
  • 3.

Look at the generated enhancements , In fact, it calls intercept() Method , This method is implemented by ourselves , Therefore, the enhanced function of 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();
        }
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

cglib and jdk What's the difference between dynamic agents

  1. jdk Dynamic proxy is an anonymous class that uses interceptors and reflection to generate a proxy interface , Give it to me when you execute the method InvokeHandler Handle .CGLIB Dynamic proxy uses ASM frame , Modify the original bytecode , Then generate new subclasses to handle .
  2. JDK The agent needs to implement the interface , however CGLIB Don't force .
  3. stay JDK1.6 Before ,cglib Because bytecode generation technology is used , Higher than reflection efficiency , But after that, jdk There are also some optimizations , Efficiency has improved .

【 Author's brief introduction 】
Qin Huai , official account 【 Qin Huai grocery store 】 author , The road to technology is not in the moment , one's nobility lasts forever , Even slowly , Go on and on . Personal writing direction :Java The source code parsing ,JDBC,Mybatis,Spring,redis, Distributed , The finger of the sword Offer,LeetCode etc. , Write every article carefully , I don't like the title party , I don't like fancy things , Most of them write series , There's no guarantee that everything I write is right , But I promise that what I have written has been put into practice or searched for information . Omission or error , I hope you can correct me. .

  The finger of the sword Offer All the answers PDF

 2020 What did I write in 2000 ?

  Open source programming notes

版权声明
本文为[Qinhuai grocery store]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/11/20211109095357350f.html

Scroll to Top