Spring source code analysis 5: JDK dynamic proxy principle and cglib Detailed

Java is the basis of dynamic AOP proxy, understand and use two dynamic proxy AOP allows us to better understand, before explaining AOP, let us first look at Java dynamic proxy use and principles underlying implementation.

Transfer from https://www.jianshu.com/u/668d0795a95b

This article is based on jdk1.8 to explore the underlying dynamic proxy mechanism

Java proxy Introduction

Implementation of the Java proxy are generally divided into three types: JDK static agents, JDK dynamic proxies and CGLIB dynamic proxy. In Spring AOP implementation, the main application of the JDK dynamic proxy and CGLIB dynamic proxy. But this article focuses on JDK dynamic proxy mechanism, dynamic behind CGLIB agent will then explore.

Proxy mode is typically implemented as static agent JDK: Create an interface, and then create the proxy class that implements the interface and implement the abstract methods interface. After then create a proxy class, but it also implements this interface. Hold a proxy object is referenced in the proxy class, and then call the object's method in the proxy class method.

In fact, the proxy class proxy class as being pre-message, the message was filtered and thereafter forwarding the message to the proxy class, also performs post-processing after the message. Acting classes and relationships are usually there is a proxy class (that is, to be held away from the object's reference mentioned above), the proxy class itself does not implement the service, but to provide services by calling the method is in the proxy class.

Static agents

interface

The proxy class

Acting Class

Testing classes and output

We can see, it is easy to use JDK static proxy agent to complete the operation on a class. But JDK static agent's shortcomings exposed out: As the agent for only one type of service, if you need a lot of acting classes, then you need to write a lot of the proxy class, more complicated.

Here we use JDK dynamic proxy to do the same thing

JDK dynamic proxies

interface

The proxy class

Acting Class

Testing classes and output

JDK dynamic proxy implementation principle

JDK dynamic proxy is actually a basic interface implementation. Since the interface points by way of polymorphic class instance, and can be efficiently embodied calling decoupling, easy to modify and maintain later.

Through the above description, we can see a slight similarity between the static agent JDK and JDK dynamic proxy, such proxy must create a class, as well as the proxy class must implement the interface and so on. But the difference is very obvious ---- we need to create a proxy class which interface and which is a proxy class for static agent, so we need a proxy class that implements the same interface proxy class before compiling, and directly in call the method implemented in the proxy class corresponding method; but the dynamic proxy is different, we do not know for which interface, which is a proxy class to create a proxy class, as it is created at runtime.

Let us use one sentence to sum up the difference between the static agent JDK and JDK dynamic proxy, and then began to explore the underlying JDK dynamic proxy implementation mechanism:
JDK static proxy is created by direct encoding, and JDK dynamic proxy using reflection at run time create a proxy class.
In fact, dynamic proxy, the core is InvocationHandler. Each instance of proxy will have an associated invocation handler (InvocationHandler). When the treatment agent instance call, the call will be coded and a method call processors assigned to it (of InvocationHandler) invoke method. So a call to the proxy object instance methods are accomplished by a method invoke InvocationHandler in, and invoke the method based on the incoming proxy object, method name and parameter determines which proxy call.

We can see from the test JDK dynamic proxy class generated proxy class by Proxy class newProxyInstance done, let's take a look into this function:

Proxy classes newProxyInstance

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //如果h为空将抛出异常
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            //在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 下面这个方法将产生代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 使用指定的调用处理程序获取代理类的构造函数对象
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //假如代理类的构造函数是private的,就使用反射来set accessible
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //根据代理类的构造函数来生成代理类的对象并返回
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

So in fact a proxy class generated by getProxyClass methods:

 /**
     * 生成一个代理类,但是在调用本方法之前必须进行权限检查
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //如果接口数量大于65535,抛出非法参数错误
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // 如果在缓存中有对应的代理类,那么直接返回
        // 否则代理类将有 ProxyClassFactory 来创建
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory So what is it?

   /**
     *  里面有一个根据给定ClassLoader和Interface来创建代理类的工厂函数  
     *
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 代理类的名字的前缀统一为“$Proxy”
        private static final String proxyClassNamePrefix = "$Proxy";

        // 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * 验证这个Class对象是不是接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 验证这个接口是否重复
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // 声明代理类所在的package
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * 记录一个非公共代理接口的包,以便在同一个包中定义代理类。同时验证所有非公共
             * 代理接口都在同一个包中
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // 如果全是公共代理接口,那么生成的代理类就在com.sun.proxy package下
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * 为代理类生成一个name  package name + 前缀+唯一编号
             * 如 com.sun.proxy.$Proxy0.class
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成指定代理类的字节码文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

Byte code generation

Code from the top byte [] proxyClassFile = ProxyGenerator.generateProxyClass (proxyName, interfaces, accessFlags); can be seen, in fact, generates a proxy class byte code file by the working method ProxyGenerate class generateProxyClass accomplished.

 public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
       // 真正用来生成代理类字节码文件的方法在这里
        final byte[] var4 = var3.generateClassFile();
       // 保存代理类的字节码文件
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if(var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), 
                                                                                   new String[0]);
                            Files.createDirectories(var3, new FileAttribute[0]);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class", new String[0]);
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

Let's look at a real generateClassFile method for generating a proxy class bytecode files:

private byte[] generateClassFile() {
        //下面一系列的addProxyMethod方法是将接口中的方法和Object中的方法添加到代理方法中(proxyMethod)
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
       //获得接口中所有方法并添加到代理方法中
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();
        //验证具有相同方法签名的方法的返回类型是否一致
        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        //后面一系列的步骤用于写代理类Class文件
        Iterator var15;
        try {
             //生成代理类的构造函数
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    //将代理类字段声明为Method,并且字段修饰符为 private static.
                   //因为 10 是 ACC_PRIVATE和ACC_STATIC的与运算 故代理类的字段都是 private static Method ***
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, 
                                   "Ljava/lang/reflect/Method;", 10));
                   //生成代理类的方法
                    this.methods.add(var16.generateMethod());
                }
            }
           //为代理类生成静态代码块对某些字段进行初始化
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if(this.methods.size() > '\uffff') { //代理类中的方法数量超过65535就抛异常
            throw new IllegalArgumentException("method limit exceeded");
        } else if(this.fields.size() > '\uffff') {// 代理类中字段数量超过65535也抛异常
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            // 后面是对文件进行处理的过程
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

Proxy class method invocation

The following are added to the interface methods to addProxyMethod Object Method proxy class:

private void addProxyMethod(Method var1, Class<?> var2) {
        String var3 = var1.getName();//获得方法名称
        Class[] var4 = var1.getParameterTypes();//获得方法参数类型
        Class var5 = var1.getReturnType();//获得方法返回类型
        Class[] var6 = var1.getExceptionTypes();//异常类型
        String var7 = var3 + getParameterDescriptors(var4);//获得方法签名
        Object var8 = (List)this.proxyMethods.get(var7);//根据方法前面获得proxyMethod的value
        if(var8 != null) {//处理多个代理接口中方法重复的情况
            Iterator var9 = ((List)var8).iterator();

            while(var9.hasNext()) {
                ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
                if(var5 == var10.returnType) {
                    ArrayList var11 = new ArrayList();
                    collectCompatibleTypes(var6, var10.exceptionTypes, var11);
                    collectCompatibleTypes(var10.exceptionTypes, var6, var11);
                    var10.exceptionTypes = new Class[var11.size()];
                    var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
                    return;
                }
            }
        } else {
            var8 = new ArrayList(3);
            this.proxyMethods.put(var7, var8);
        }

        ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
    }

This is the final real proxy class that inherits from Proxy and implements the interface we define the Subject. We

HelloInterface helloInterface = (HelloInterface ) Proxy.newProxyInstance(loader, interfaces, handler);
  • 1

The resulting final proxy class object is an instance of this class above. So we execute the following statement:

helloInterface.hello("Tom");
  • 1

In fact, the respective method of the above class, namely:

 public final void hello(String paramString)
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      //就是调用我们自定义的InvocationHandlerImpl的 invoke方法:
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

Note that here this.h.invokethe h, which is a property of the class Proxy

 protected InvocationHandler h;

Because the proxy class inherits Proxy, so it inherits this property, and the value of this property is our definition

InvocationHandler handler = new InvocationHandlerImpl(hello);
  • 1

We also found that the first parameter invoke method when the incoming call is the bottom this, which is finally generated proxy object ProxySubject, which is its own JVM dynamically generated, rather than our own proxy object definition.

In-depth understanding of the dynamic proxy mechanism CGLIB

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. Below is my relationship with the Internet to find a Cglib with a number of frameworks and languages:

This chart summarize:

  • The bottom Bytecode byte code, Java bytecodes in order to ensure "once, run anywhere" resulting from a virtual instruction format, e.g. iload_0, iconst_1, if_icmpne, dup, etc.
  • Is located above the ASM bytecode, which is a direct manipulation frame bytecode, ASM applications require Java bytecode, Class familiar structure
  • ASM is located above CGLIB, Groovy, BeanShell, the latter two not the content but the Java System languages, they disguise execute Java byte code by a code generation ASM frame, which is not necessarily in the described procedure is carried JVM non-write Java code ---- as long as you can generate Java byte code, source JVM bytecode does not care, of course, the code generated by the Java JVM byte code is directly generated by the compiler, is probably the most "orthodox "the JVM bytecode
  • Located CGLIB, Groovy, BeanShell is on top of Hibernate, Spring AOP frameworks, and this one we are more familiar
  • The uppermost layer is Applications, namely the specific application, usually a Web project or run a local program

This article is to explore based CGLIB 3.1

cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.

In Spring AOP usually use it to generate AopProxy object. Moreover, in Hibernate PO (Persistant Object persistent objects) should bytecode generated is sufficient to complete the work.

This article will delve into CGLIB dynamic proxy implementation mechanism, with the following article eaten together tastes better:
in-depth understanding of JDK dynamic proxy mechanism

CGLIB dynamic proxy example

Below we start with a simple example to us CGLIB dynamic proxies:

In order to successfully carry out follow-coded, we need to use Maven package introduced CGLIB

FIG proxy class is 1.1

FIG. 1.2 The method of generating an interface interceptor MethodInterceptor

Figure 1.3 and generate the proxy class object after printing agent class object method call results of execution

JDK agent requires that the proxied class must implement the interface, there are strong limitations. The dynamic proxy CGLIB is no such mandatory requirement. Briefly, CGLIB-generated proxy class will inherit the proxy class, and performs enhancement processing method in the proxy proxy class (pre-processing, post-processing, etc.). In CGLIB bottom, in fact, by means of this very powerful ASM Java byte code generation framework.

Generate a proxy class object

We see from Figure 1.3, the agent class object is created by the Enhancer class. Enhancer is CGLIB bytecode enhancer, can easily expand on classes, class 1.3 of FIG disposed Superclass.

A few steps to create a proxy object:

  • Generating the binary class bytecode files agent;
  • Loading binary byte code generation Class object (e.g. using the Class.forName () method);
  • Examples of configurations obtained by the reflection mechanism, and creates a proxy class object

Let's look at the Java code after the proxy class file decompile Class

package proxy;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class HelloServiceImpl$EnhancerByCGLIB$82ef2d06
  extends HelloServiceImpl
  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$sayHello$0$Method;
  private static final MethodProxy CGLIB$sayHello$0$Proxy;
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$finalize$1$Method;
  private static final MethodProxy CGLIB$finalize$1$Proxy;
  private static final Method CGLIB$equals$2$Method;
  private static final MethodProxy CGLIB$equals$2$Proxy;
  private static final Method CGLIB$toString$3$Method;
  private static final MethodProxy CGLIB$toString$3$Proxy;
  private static final Method CGLIB$hashCode$4$Method;
  private static final MethodProxy CGLIB$hashCode$4$Proxy;
  private static final Method CGLIB$clone$5$Method;
  private static final MethodProxy CGLIB$clone$5$Proxy;

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("proxy.HelloServiceImpl$EnhancerByCGLIB$82ef2d06");
    Class localClass2;
    Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$finalize$1$Method = tmp95_92[0];
    CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
    Method[] tmp115_95 = tmp95_92;
    CGLIB$equals$2$Method = tmp115_95[1];
    CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
    Method[] tmp135_115 = tmp115_95;
    CGLIB$toString$3$Method = tmp135_115[2];
    CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
    Method[] tmp155_135 = tmp135_115;
    CGLIB$hashCode$4$Method = tmp155_135[3];
    CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
    Method[] tmp175_155 = tmp155_135;
    CGLIB$clone$5$Method = tmp175_155[4];
    CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    tmp175_155;
    Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "sayHello", "()V" }, (localClass2 = Class.forName("proxy.HelloServiceImpl")).getDeclaredMethods());
    CGLIB$sayHello$0$Method = tmp223_220[0];
    CGLIB$sayHello$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "sayHello", "CGLIB$sayHello$0");
    tmp223_220;
    return;
  }

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

  public final void sayHello()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null) {
      return;
    }
    super.sayHello();
  }

  final void CGLIB$finalize$1()
    throws Throwable
  {
    super.finalize();
  }

  protected final void finalize()
    throws Throwable
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null) {
      return;
    }
    super.finalize();
  }

  final boolean CGLIB$equals$2(Object paramObject)
  {
    return super.equals(paramObject);
  }

  public final boolean equals(Object paramObject)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$2$Method, new Object[] { paramObject }, CGLIB$equals$2$Proxy);
      tmp41_36;
      return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
    }
    return super.equals(paramObject);
  }

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

  public final String toString()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return (String)tmp17_14.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
    }
    return super.toString();
  }

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

  public final int hashCode()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.hashCode();
  }

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

  protected final Object clone()
    throws CloneNotSupportedException
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return tmp17_14.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
    }
    return super.clone();
  }

  public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
  {
    String tmp4_1 = paramSignature.toString();
    switch (tmp4_1.hashCode())
    {
    case -1574182249: 
      if (tmp4_1.equals("finalize()V")) {
        return CGLIB$finalize$1$Proxy;
      }
      break;
    }
  }

  public HelloServiceImpl$EnhancerByCGLIB$82ef2d06()
  {
    CGLIB$BIND_CALLBACKS(this);
  }

  public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
  }

  public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
  }

  private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
  {
    82ef2d06 local82ef2d06 = (82ef2d06)paramObject;
    if (!local82ef2d06.CGLIB$BOUND)
    {
      local82ef2d06.CGLIB$BOUND = true;
      Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
      if (tmp23_20 == null)
      {
        tmp23_20;
        CGLIB$STATIC_CALLBACKS;
      }
      local82ef2d06.CGLIB$CALLBACK_0 = (// INTERNAL ERROR //

Entrusted proxy class

We posted the above proxy class generated source code. With reference to our example above, let's summarize CGLIB conducting agent when they are carried out what does work

  • Generated proxy class HelloServiceImpl $ EnhancerByCGLIB $ 82ef2d06 inheritance is HelloServiceImpl proxy class. Here we need to pay attention to one point: if the commission modified the class is final, it can not be inherited, that is not proxied; Similarly, if the final modification methods exist delegate class, then the method can not be proxied;
  • Generates proxy classes for the delegate methods the two methods, a method is sayHello rewritten, and the other is CGLIB $ sayHello $ 0 method, we can see that it is a direct call sayHello method of the parent class;
  • When executing sayHello method of the proxy object, it will first determine whether there is achieved CGLIB $ CALLBACK_0 MethodInterceptor interface ;, if present, intercept method will be called MethodInterceptor in Figure 2.1.

2.1 intercept method of FIG.

Figure 2.2 proxy class for each delegate method generates two methods

In the intercept method, we will call in addition to the delegate method, but also make some enhancements to the operation. In Spring AOP, typical application scenarios is performed before and after the operation log record certain sensitive method is performed.

We see from the figure 2.1, call the delegate method is invoked invokeSuper method MethodProxy object proxy method to perform, let's take a look at invokeSuper methods mystery:

2.3 invokeSuper method of FIG.

Here it seems can not be directly seen calling the proxy method. It does not matter, I will slowly introduce.
We know that in JDK dynamic proxy method call is done by reflection. If this do not understand the students can look at my previous blog ---- -depth understanding of JDK dynamic proxy mechanism . But in CGLIB, call the method is not accomplished by reflection, but directly to the method call: FastClass of Class objects special handling, such as a reference method will be saved with the array, each method call when they are the method is to keep the index by a reference index. GetIndex example, the following method is obtained by the method signature of the method in a storage array of Class information in the index.

2.4 getIndex method of FIG.

2.5 FastClassInfo FIG class object reference held by two FastClass .png

With our above sayHello method, for example, f1 points delegate class object, f2 points to the proxy class object, i1 and i2 represent the sayHello method and CGLIB $ sayHello $ 0 subscript method in object information array.

CGLIB far finished introduce dynamic proxy given comparison between the three agents of the following ways.

Acting way achieve advantage Shortcoming Feature
JDK static agents Proxy class and delegate class that implements the same interface, and the need for hard-coded in the proxy class interfaces Simple, easy to understand Acting classes require hard-coded interfaces, in practical applications could lead to repetition coding, wasting storage space and inefficient As if nothing Features
JDK dynamic proxies Proxy class and delegate class that implements the same interface, mainly achieved through a proxy InvocationHandler class and override the invoke method for dynamic proxy method will invoke enhanced processing method You do not need to hard-code interfaces, code reuse rate Acting can only delegate class implements an interface Reflection method using the underlying call
CGLIB dynamic proxy Acting classes will be entrusted to class as their parent and one of the two non-final method is to create a delegate method, a delegate method is the same as the method signature, it will be in the method call by super delegate methods; the other is only a proxy class some methods. In the proxy method, which will determine whether there is an object that implements MethodInterceptor interface will be called if there is a method to intercept proxy delegate method Can enhance the operation of the interface or class at runtime, without the delegate class implements the interface and Can not be a proxy for a final class and final method The method underlying all into an array, a method called directly by the array index

image description

Micro-channel public number [yellow] small ramp of ants gold dress JAVA engineer, specializing in JAVA
backend technology stack: SpringBoot, SSM family bucket, MySQL, distributed, middleware, services, but also understand the point of finance and investment, adhere to the study and writing, believe in the power of lifelong learning! No reply after public concern "architect" to receive a
Java-based, advanced, project architects and other free learning materials and, more databases, distributed, service and other popular micro learning video technology, rich content, both theory and practice, also presented will be the original author of the study guide Java, Java programmer interview guide and other resources dry goods

Guess you like

Origin www.cnblogs.com/xll1025/p/11407769.html