Spring transaction Transactional and dynamic proxies (b) -cglib dynamic proxies

Index series:

  1. Spring transaction Transactional and dynamic proxies (a) -JDK proxy implementation
  2. Spring transaction Transactional and dynamic proxies (b) -cglib dynamic proxies
  3. Spring transaction Transactional and dynamic proxies (c) - a transaction failure scenarios

What is cglib

Cglib is a powerful, high-performance code generation package, which is widely used by many AOP framework that provides for a method to intercept them. It provides for the agent class does not implement interface, provides a good complement to the JDK dynamic proxy. JDK must force based interface interface type: the Spring transaction Transactional and dynamic proxies (on) -JDK proxy implementation

cglib applications

cglib very broad application, according to the Github cglib described in ( cglib ), the following applications:

  1. Byte Code Engineering Library
    is JavaClass byte code file, the library can easily analyze, create and manipulate bytecode files
  2. XORM
    is an extensible framework for ORM, use cglib to generate a persistent object mapping to provide a lasting Entity interface is RDBMS, enables developers to focus on the business object model
  3. Hibernate
    Hibernate is a another powerful, ultra-high performance for Java object / relational persistence framework. Persistent objects can be developed, including association, inheritance, polymorphism, and the combination of the Java Collections Framework
  4. The Java Class File Editor at The
    the Java class file editor that allows the user to read or when loading classes at runtime on the disk / Modify Class file, it can also create a new class dynamically
  5. Nanning Aspects
    is a java-based AOP framework Introduction
  6. Spring
  7. iBatis/Mybatis
  8. ASM
  9. Proxool
    java-based connection pool
  10. Guice
  11. model Mapper

cglib use

Use cglib need introduced jar package, adding a dependency in the maven:

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

Create a target class, which is a final method, a non-final method for comparison cglib weaving results for two methods:

public class Student {

    public void study(){
        System.out.println("study");
    }

    public final void eat(){
        System.out.println("eat");
    }

}

Interceptor proxy class as follows:

public class CglibInterceptor implements MethodInterceptor {
    //织入前的处理
    private void beforeInvoke(Method method){
        System.out.println("before " + method.getName());
    }

    //织入后的处理
    private void afterInvoke(Method method){
        System.out.println("after "  + method.getName());
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeInvoke(method);
        //调用cglib的invokeSuper而不是invoke方法
        Object object = methodProxy.invokeSuper(o,objects);
        afterInvoke(method);
        return object;
    }
}

Call to order the test class is

  1. Create an enhanced built Enhancer examples
  2. Set by the target class method setSuperclass
  3. Set Interceptor intercepted by setCallback
  4. Enhancer create method call generated proxy class
    code as follows:
public class CglibTesst {

    public static void main(String[] args) {
        //把生产的代理类保存到磁盘指定文件夹
        System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new CglibInterceptor());

        Student studentProxy = (Student) enhancer.create();
        studentProxy.study();
        studentProxy.eat();
    }
}

Wherein the output is as follows, the method can be seen that only the non-final woven into study before and after logic, the final method is not eat:

before study
study
after study

eat

cglib generated proxy class file analysis

By adding the test class

System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");

After the code, the more out of a number of local .class file as follows:

First look at the Student $ EnhancerByCGLIB $ 92f3e3f6, inherited the Student and implements Factory Interface (interface method primarily newInstance, setCallback and getCallbacks), the class code too much, the following code is an excerpt:

public class Student$EnhancerByCGLIB$92f3e3f6 extends Student implements Factory {
    
    //静态初始化类
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.randy.dynamicproxy.cglib.Student$$EnhancerByCGLIB$$92f3e3f6");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$study$0$Method = ReflectUtils.findMethods(new String[]{"study", "()V"}, (var1 = Class.forName("com.randy.dynamicproxy.cglib.Student")).getDeclaredMethods())[0];
        CGLIB$study$0$Proxy = MethodProxy.create(var1, var0, "()V", "study", "CGLIB$study$0");
    }

    static {
        CGLIB$STATICHOOK1();
    }

    final void CGLIB$study$0() {
        super.study();
    }

    public final void study() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        //检查当前Callback拦截对象
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        //根据是否存在判定是通过拦截类来调用还是直接调用父类Student的study方法
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy);
        } else {
            super.study();
        }
    }

    final boolean CGLIB$equals$1(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
       ...
    }

    final String CGLIB$toString$2() {
        return super.toString();
    }

    public final String toString() {
       ...
    }

    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }

    public final int hashCode() {
        ...
    }

    final Object CGLIB$clone$4() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
       ...
    }
}

The generated class can be seen, in addition to implementing the method Factory interface, both the replication and Student class super class Object method of non-final (final method for final methods Wati eat and Object of the Student, notify, notifyAll the like no replication), which is why cglib can not proxy for final approach because java does not allow replication method final

The other two classes $ EnhancerByCGLIB Student \ (92f3e3f6 \) FastClassByCGLIB \ (1d02f934 and Student \) FastClassByCGLIB $ ec571eb6 inherit the abstract class FastClass cglib, and
mainly achieved FastClass's look at a few methods

    public abstract int getIndex(String var1, Class[] var2);
    public abstract int getIndex(Class[] var1);
    public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
    public abstract Object newInstance(int var1, Object[] var2) throws InvocationTargetException;
    public abstract int getIndex(Signature var1);
    public abstract int getMaxIndex();

one of them

cglib principle

cglib dynamically generate a proxy class to subclass, all is not final subclass to override the proxy class method ( cglib can not proxy for the final method ). In the subclass method call interception techniques intercept all the parent class methods, homeopathic weaving transverse logic.

CGLIB underlying bytecode processing framework ASM, to convert byte code and generate a new class. About java bytecode please see: at The File the Format the Java class

Enhancer class source code analysis

public class Enhancer extends AbstractClassGenerator {
    //设置目标类作为父类,也就是对应生成的Student$$EnhancerByCGLIB$$92f3e3f6类继承了Student
    public void setSuperclass(Class superclass) {
        if (superclass != null && superclass.isInterface()) {
            this.setInterfaces(new Class[]{superclass});
        } else if (superclass != null && superclass.equals(Object.class)) {
            this.superclass = null;
        } else {
            this.superclass = superclass;
        }

    }
    //通过Enhancer来创建代理类
    public Object create() {
        this.classOnly = false;
        this.argumentTypes = null;
        return this.createHelper();
    }
    
    private Object createHelper() {
        this.preValidate();
        //根据当前设置的父类等信心构造一个唯一的key
        Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }
}

    protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
            //首先从缓存中查找key,如果就生成一个
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        //核心是调用了AbstractClassGenerator的generate来生成字节码文件,并通过ReflectUtils.defineClass返回
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        //加入缓存  
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }

            this.key = key;
            Object obj = data.get(this, this.getUseCache());
            //如果是类就通过firstInstance初始化,而firstInstance在AbstractClassGenerator类中是一个抽象方法,具体实现如下
            //firstInstance和nextInstance都是通过cglib的ReflectUtils.newInstance来创建实例的
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        } catch (RuntimeException var9) {
            throw var9;
        } catch (Error var10) {
            throw var10;
        } catch (Exception var11) {
            throw new CodeGenerationException(var11);
        }
    }

MethodProxy

When the generated proxy class is called, MethodProxy intercept calls method provided in the CallBack. MethodProxy.invokeSuper intercept method and a method in the above CglibInterceptor class is used, the following source code:

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

init method:

init () method is a classic double check Singleton design pattern, to determine whether the initial object has been initialized, if not sentenced empty and locked again. The main contents of the initialization is FastClassInfo objects and their properties

private final Object initLock = new 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);
                    //通过getIndex来查找到指定方法的索引
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

FastClass mechanism

FastClass indexing mechanism is a class method, called directly through the index corresponding method, wherein the above-described invokeSuper init initialization mainly FastClassInfo (internal class, holding two types of variable FastClass).

 private static class FastClassInfo
    {
        //目标类的FastClass
        FastClass f1;
        //代理类的FastClass
        FastClass f2;
        //目标类方法的索引
        int i1;
        //代理类方法的索引
        int i2;
    }

In the previous JDK agent implementations mentioned JDK interception object is reflected by InvocationHandler mechanism to call the intercepted method, reflecting the relatively low efficiency.
The cglib method is based on a class of index, index by calling the appropriate method directly.
Such as the generation of Student \ (FastClassByCGLIB \) ec571eb6 is inherited FastClass, getIndex (Signature) located by an index by the method signature,

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1310345955:
            if (var10000.equals("eat()V")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 2;
            }
            break;
        case 1876544780:
            if (var10000.equals("study()V")) {
                return 0;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4;
            }
        }

        return -1;
    }

In the method according to the call invoke Index position acquired, invoke method is an abstract method, subclass (i.e. generated in FastClass Student class \ (FastClassByCGLIB \) ec571eb6 inherited FastClass) embodied as follows:

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        Student var10000 = (Student)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.study();
                return null;
            case 1:
                var10000.eat();
                return null;
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
}

reference:

  1. https://github.com/cglib/cglib/wiki
  2. https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html
  3. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
  4. https://www.baeldung.com/cglib
  5. https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html

Guess you like

Origin www.cnblogs.com/qizhelongdeyang/p/12412049.html