JDK动态代理生成class文件和cglib动态代理生成class文件

反编译软件

概述

  • JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期期间创建一个接口的实现类来完成对目标对象的代理。
  • CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层依靠ASM(开源的Java字节码编辑类库)操作字节码实现。

1 JDK动态代理

1.1 创建代理过程:

  1. 定义接口
  2. 实现接口
  3. 定义代理类,继承InvocationHandler接口

1.2 具体代码

接口:

public interface Person {
    void say();
}

实现类:

public class JdkStudent implements Person{
    @Override
    public void say() {
        System.out.println("我是学生...");
    }
}

代理类:

public class JdkProxy implements InvocationHandler{

    private Object target;

    public JdkProxy() {
        super();
    }

    public JdkProxy(Object target) {
        this.target = target;
    }
    /**
     该方法负责集中处理动态代理类上的所有方法调用。
     第一个参数既是代理类实例,
     第二个参数是被调用的方法对象
     第三个方法是调用参数。
     调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("------插入前置通知代码-------------");  
        Object obj = method.invoke(target, args);
        System.out.println("------插入后置通知代码-------------");  
        return obj;
    }
}

测试:

public class JdkTest {
    public static void main(String[] args) {
         //生成$Proxy0的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        JdkStudent student = new JdkStudent();
        Person p = (Person) Proxy.newProxyInstance(student.getClass().getClassLoader(),
                student.getClass().getInterfaces(), new JdkProxy(student));
        System.out.println(p);
        p.say();
    }
}

注:JDK动态代理生成的文件默认在sun/proxy下,如果没有该目录会报Exception in thread “main” java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException: sun\proxy$Proxy0.class (系统找不到指定的路径。)

生成的代理class

package sun.proxy;

import com.gz.design.proxy.jdk.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements Person
{
  private static Method m3;
  private static Method m1;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }

  public final void say()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m3 = Class.forName("com.gz.design.proxy.jdk.Person").getMethod("say", new Class[0]);
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

1.2 总结

查看JDK动态代理生成的class文件,我们找出关键代码,该段代码完成了调用目标方法

public final void say()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

可以看出该方法中有一个this.h.invoke(this, m3, null),那么h是什么,通过生成的class文件我们没有发现h属性,接着父类,发现在 Proxy类中有h属性,通过h的类型,聪明的我们可能已经猜测出h是什么。下面我们验证一下
验证:

public class JdkTest {
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        JdkStudent student = new JdkStudent();
        Person p = (Person) Proxy.newProxyInstance(student.getClass().getClassLoader(),
                student.getClass().getInterfaces(), new JdkProxy(student));
        p.say();

        System.out.println("=========h================");
        Field h = p.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        Object obj = h.get(p);
        System.out.println(obj.getClass());
    }
}

这里写图片描述
可以看出h是我们创建的JdkProxy类

2 cglib动态代理

2.1 创建代理过程:

  1. 创建Enhancer对象
  2. 用Enhancer对象设置代理类的父类(被代理类)
  3. 创建回调对象(回调类实现 MethodInterceptor 接口)
  4. 用Enhancer对象设置回调对象
  5. 用Enhancer对象创建代理对象

1.2 具体代码

目标类:

public class Student{
    public void say() {
        System.out.println("我是学生...");
        test();
    }

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

代理类:

public class CglibProxy implements MethodInterceptor{

     /** 
     * 重写方法拦截在方法前和方法后加入业务 
     * Object obj为目标对象 
     * Method method为目标方法 
     * Object[] params 为参数, 
     * MethodProxy proxy CGlib方法代理对象 
     */  
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("===============");
        return methodProxy.invokeSuper(obj, args);
    }
}

测试类:

public class CglibProxyTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    // 生成class类的路径
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://tmp");   
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new CglibProxy());

        Student student = (Student) enhancer.create();
        student.say();
        }
     }

生成class



package com.gz.design.proxy.cglib;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
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 Student$$EnhancerByCGLIB$$1857f4b9
  extends Student
  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;//拦截器及集成MethodInterceptor
  private static final Method CGLIB$say$0$Method;// 目标类方法
  private static final MethodProxy CGLIB$say$0$Proxy;// 代理类方法
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$test$1$Method;
  private static final MethodProxy CGLIB$test$1$Proxy;
  private static final Method CGLIB$finalize$2$Method;
  private static final MethodProxy CGLIB$finalize$2$Proxy;
  private static final Method CGLIB$equals$3$Method;
  private static final MethodProxy CGLIB$equals$3$Proxy;
  private static final Method CGLIB$toString$4$Method;
  private static final MethodProxy CGLIB$toString$4$Proxy;
  private static final Method CGLIB$hashCode$5$Method;
  private static final MethodProxy CGLIB$hashCode$5$Proxy;
  private static final Method CGLIB$clone$6$Method;
  private static final MethodProxy CGLIB$clone$6$Proxy;

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("com.gz.design.proxy.cglib.Student$$EnhancerByCGLIB$$1857f4b9");
    Class localClass2;
    Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "say", "()V", "test", "()V" }, (localClass2 = Class.forName("com.gz.design.proxy.cglib.Student")).getDeclaredMethods());
    CGLIB$say$0$Method = tmp60_57[0];
    CGLIB$say$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "say", "CGLIB$say$0");
    Method[] tmp80_60 = tmp60_57;
    CGLIB$test$1$Method = tmp80_60[1];
    CGLIB$test$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "test", "CGLIB$test$1");
    tmp80_60;
    Method[] tmp173_170 = 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$2$Method = tmp173_170[0];
    CGLIB$finalize$2$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$2");
    Method[] tmp193_173 = tmp173_170;
    CGLIB$equals$3$Method = tmp193_173[1];
    CGLIB$equals$3$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
    Method[] tmp213_193 = tmp193_173;
    CGLIB$toString$4$Method = tmp213_193[2];
    CGLIB$toString$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
    Method[] tmp233_213 = tmp213_193;
    CGLIB$hashCode$5$Method = tmp233_213[3];
    CGLIB$hashCode$5$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$5");
    Method[] tmp253_233 = tmp233_213;
    CGLIB$clone$6$Method = tmp253_233[4];
    CGLIB$clone$6$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
    tmp253_233;
    return;
  }

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

  public final void say()
  {
    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.say();
  }

  final void CGLIB$test$1()
  {
    super.test();
  }

  public final void test()
  {
    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.test();
  }

  final void CGLIB$finalize$2()
    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$3(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$3$Method, new Object[] { paramObject }, CGLIB$equals$3$Proxy);
      tmp41_36;
      return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
    }
    return super.equals(paramObject);
  }

  final String CGLIB$toString$4()
  {
    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$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy);
    }
    return super.toString();
  }

  final int CGLIB$hashCode$5()
  {
    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$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.hashCode();
  }

  final Object CGLIB$clone$6()
    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$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy);
    }
    return super.clone();
  }

  /* Error */
  public static MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature arg0)
  {
    // Byte code:
    //   0: aload_0
    //   1: invokevirtual 138   java/lang/Object:toString   ()Ljava/lang/String;
    //   4: dup
    //   5: invokevirtual 139   java/lang/Object:hashCode   ()I
    //   8: lookupswitch    default:+152->160, -1574182249:+68->76, -1422510685:+80->88, -909388886:+92->100, -508378822:+104->112, 1826985398:+116->124, 1913648695:+128->136, 1984935277:+140->148
    //   76: ldc -115
    //   78: invokevirtual 142  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   81: ifeq +80 -> 161
    //   84: getstatic 74   com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$finalize$2$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   87: areturn
    //   88: ldc -112
    //   90: invokevirtual 142  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   93: ifeq +68 -> 161
    //   96: getstatic 62   com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$test$1$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   99: areturn
    //   100: ldc -110
    //   102: invokevirtual 142 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   105: ifeq +56 -> 161
    //   108: getstatic 46  com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$say$0$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   111: areturn
    //   112: ldc -108
    //   114: invokevirtual 142 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   117: ifeq +44 -> 161
    //   120: getstatic 135 com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$clone$6$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   123: areturn
    //   124: ldc -106
    //   126: invokevirtual 142 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   129: ifeq +32 -> 161
    //   132: getstatic 87  com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$equals$3$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   135: areturn
    //   136: ldc -104
    //   138: invokevirtual 142 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   141: ifeq +20 -> 161
    //   144: getstatic 104 com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$toString$4$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   147: areturn
    //   148: ldc -102
    //   150: invokevirtual 142 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   153: ifeq +8 -> 161
    //   156: getstatic 117 com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$hashCode$5$Proxy Lnet/sf/cglib/proxy/MethodProxy;
    //   159: areturn
    //   160: pop
    //   161: aconst_null
    //   162: areturn
  }

  public Student$$EnhancerByCGLIB$$1857f4b9()
  {
    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)
  {
    1857f4b9 local1857f4b9 = (1857f4b9)paramObject;
    if (!local1857f4b9.CGLIB$BOUND)
    {
      local1857f4b9.CGLIB$BOUND = true;
      Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
      if (tmp23_20 == null)
      {
        tmp23_20;
        CGLIB$STATIC_CALLBACKS;
      }
      local1857f4b9.CGLIB$CALLBACK_0 = (tmp31_28 == null ? tmp31_28 : (MethodInterceptor)((Callback[])tmp23_20)[0]);
    }
  }

  public Object newInstance(Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new 1857f4b9();
  }

  public Object newInstance(Callback paramCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new 1857f4b9();
  }

  /* Error */
  public Object newInstance(Class[] arg1, Object[] arg2, Callback[] arg3)
  {
    // Byte code:
    //   0: aload_3
    //   1: invokestatic 220    com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$SET_THREAD_CALLBACKS ([Lnet/sf/cglib/proxy/Callback;)V
    //   4: new 2   com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9
    //   7: dup
    //   8: aload_1
    //   9: dup
    //   10: arraylength
    //   11: tableswitch    default:+24->35, 0:+17->28
    //   28: pop
    //   29: invokespecial 221  com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:<init>  ()V
    //   32: goto +17 -> 49
    //   35: goto +3 -> 38
    //   38: pop
    //   39: new 227    java/lang/IllegalArgumentException
    //   42: dup
    //   43: ldc -27
    //   45: invokespecial 232  java/lang/IllegalArgumentException:<init>   (Ljava/lang/String;)V
    //   48: athrow
    //   49: aconst_null
    //   50: invokestatic 220   com/gz/design/proxy/cglib/Student$$EnhancerByCGLIB$$1857f4b9:CGLIB$SET_THREAD_CALLBACKS ([Lnet/sf/cglib/proxy/Callback;)V
    //   53: areturn
  }

  public Callback getCallback(int paramInt)
  {
    CGLIB$BIND_CALLBACKS(this);
    switch (paramInt)
    {
    case 0: 
      break;
    }
    return null;
  }

  public void setCallback(int paramInt, Callback paramCallback)
  {
    switch (paramInt)
    {
    case 0: 
      this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
      break;
    }
  }

  public Callback[] getCallbacks()
  {
    CGLIB$BIND_CALLBACKS(this);
    return new Callback[] { this.CGLIB$CALLBACK_0 };
  }

  public void setCallbacks(Callback[] paramArrayOfCallback)
  {
    this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
  }

  static {}
}

2.2 总结:

查看cglib生成的class文件,我们找出关键代码,该段代码完成了调用目标方法

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

public final void say(){
    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.say();
}

可以看出通过class文件中CGLIB$CALLBACK_0属性来调用目标中的方法,通过CGLIB$CALLBACK_0类型我们 大致可以猜测它对应我们创建的哪个类,接下来我们验证我们的猜想:
验证

public class CglibProxyTest1 {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://tmp");   

        Student student = (Student) Enhancer.create(Student.class, new CglibProxy1(new Student()));
        student.say();

        System.out.println("=========CGLIB$CALLBACK_0==========");
        Field h = student.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object obj = h.get(student);
        System.out.println(obj.getClass());
    }
}

这里写图片描述
可以看出最后拿到了是CglibProxy1类

动态代理资料一
动态代理资料二

展开阅读全文

没有更多推荐了,返回首页