工作生活

JVM_字节码:字节码层面看看动态代理

2019-07-06  本文已影响0人  CalmHeart

SpringAop是基于动态代理的,主要有2种方式:JDK的动态代理,CGLIB的代理,增加织入的功能。

JDK动态代理是针对接口的编程适应的,但是对于继承的就需要使用CGLIB完成。需要预备的内容是设计模式,及编码实战一下,体会一下动态代理的含义。

JDK动态代理-源代码片段:
被代理的顶层接口:
Subject.java

//主题
public interface Subject {


  void request();

}

对应接口的实现类: RealSubject.java

public class RealSubject implements Subject {

  @Override
  public void request() {

    System.out.println("from real subject");

  }
}

实现了InvocationHandler接口的实现类: 这个类被代理类当成method.invoke(object,args)中的object使用。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

public class DynamicSubject implements InvocationHandler {
  private Subject subject;

  public DynamicSubject(Subject subject) {
    this.subject = subject;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    System.out.println("before calling: " + method);

    method.invoke(this.subject, args);

    System.out.println("after calling: " + method);

    return null;

  }
}

客户端代码:

  public static void main(String[] args) {

    System.getProperties().put("","true");

    RealSubject realSubject = new RealSubject();

    InvocationHandler invocationHandler = new DynamicSubject(realSubject);

    Class<?> clazz = realSubject.getClass();

    Subject subject = (Subject) Proxy
        .newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), invocationHandler);

    subject.request();

    System.out.println(subject.getClass());
    System.out.println(subject.getClass().getInterfaces());
  }
}

执行结果:

before calling: public abstract void com.compass.spring_lecture.binarycode.dynamicproxy.Subject.request()
from real subject
after calling: public abstract void com.compass.spring_lecture.binarycode.dynamicproxy.Subject.request()
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy


可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号


通过源码查看$Proxy0的生成过程:

  1. java.lang.reflect.Proxy#newProxyInstance
  2. java.lang.reflect.Proxy#getProxyClass0
  3. java.lang.reflect.WeakCache#get
  4. java.lang.reflect.WeakCache.Factory#get
  5. java.lang.reflect.Proxy.ProxyClassFactory#apply
  6. sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
  7. sun.misc.ProxyGenerator#generateClassFile
    通过这个方法获取字节数组,调用native方法创建Class对象。
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<Void>() {
        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));
              Files.createDirectories(var3);
              var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
            } else {
              var2 = Paths.get(var0 + ".class");
            }

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

    return var4;
  }

从上面的代码可以看出通过设置:
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
这个系统参数可以将动态生成的代理对象,写到磁盘上。

在main method 第一行添加如下代码:


image.png

生成的字节码文件如下图所示:


image.png

IDEA 反编译结果:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.compass.spring_lecture.binarycode.dynamicproxy.Subject;
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 Subject {
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
  }

  public final boolean equals(Object var1) throws  {
    try {
      return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
      throw var3;
    } catch (Throwable var4) {
      throw new UndeclaredThrowableException(var4);
    }
  }

  public final String toString() throws  {
    try {
      return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }

  public final void request() throws  {
    try {
      super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }

  public final int hashCode() throws  {
    try {
      return (Integer)super.h.invoke(this, m0, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }

  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
      m2 = Class.forName("java.lang.Object").getMethod("toString");
      m3 = Class.forName("com.compass.spring_lecture.binarycode.dynamicproxy.Subject").getMethod("request");
      m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
      throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
      throw new NoClassDefFoundError(var3.getMessage());
    }
  }
}

javap -verbose 反编译结果:

C:\spring_lecture\com\sun\proxy>java -v $Proxy0
Unrecognized option: -v
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

C:\spring_lecture\com\sun\proxy>javap -v $Proxy0
警告: 二进制文件$Proxy0包含com.sun.proxy.$Proxy0
Classfile /C:/spring_lecture/com/sun/proxy/$Proxy0.class
  Last modified 2019-7-6; size 1995 bytes
  MD5 checksum 6297b0bb77dd52445eaaca8119311a96
public final class com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy implements com.compass.spring_lecture.binarycode.dynamicpr
oxy.Subject
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_FINAL
Constant pool:
    #1 = Utf8               <init>
    #2 = Utf8               (Ljava/lang/reflect/InvocationHandler;)V
    #3 = Utf8               Code
    #4 = Utf8               Exceptions
    #5 = Utf8               java/lang/reflect/Proxy
    #6 = Class              #5            // java/lang/reflect/Proxy
    #7 = NameAndType        #1:#2         // "<init>":(Ljava/lang/reflect/InvocationHandler;)V
    #8 = Methodref          #6.#7         // java/lang/reflect/Proxy."<init>":(Ljava/lang/reflect/InvocationHandler;)V
    #9 = Utf8               m1
   #10 = Utf8               Ljava/lang/reflect/Method;
   #11 = Utf8               equals
   #12 = Utf8               (Ljava/lang/Object;)Z
   #13 = Utf8               h
   #14 = Utf8               Ljava/lang/reflect/InvocationHandler;
   #15 = NameAndType        #13:#14       // h:Ljava/lang/reflect/InvocationHandler;
   #16 = Fieldref           #6.#15        // java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
   #17 = Utf8               com/sun/proxy/$Proxy0
   #18 = Class              #17           // com/sun/proxy/$Proxy0
   #19 = NameAndType        #9:#10        // m1:Ljava/lang/reflect/Method;
   #20 = Fieldref           #18.#19       // com/sun/proxy/$Proxy0.m1:Ljava/lang/reflect/Method;
   #21 = Utf8               java/lang/Object
   #22 = Class              #21           // java/lang/Object
   #23 = Utf8               java/lang/reflect/InvocationHandler
   #24 = Class              #23           // java/lang/reflect/InvocationHandler
   #25 = Utf8               invoke
   #26 = Utf8               (Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
   #27 = NameAndType        #25:#26       // invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Obj
ect;
   #28 = InterfaceMethodref #24.#27       // java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Metho
d;[Ljava/lang/Object;)Ljava/lang/Object;
   #29 = Utf8               java/lang/Boolean
   #30 = Class              #29           // java/lang/Boolean
   #31 = Utf8               booleanValue
   #32 = Utf8               ()Z
   #33 = NameAndType        #31:#32       // booleanValue:()Z
   #34 = Methodref          #30.#33       // java/lang/Boolean.booleanValue:()Z
   #35 = Utf8               java/lang/Error
   #36 = Class              #35           // java/lang/Error
   #37 = Utf8               java/lang/RuntimeException
   #38 = Class              #37           // java/lang/RuntimeException
   #39 = Utf8               java/lang/Throwable
   #40 = Class              #39           // java/lang/Throwable
   #41 = Utf8               java/lang/reflect/UndeclaredThrowableException
   #42 = Class              #41           // java/lang/reflect/UndeclaredThrowableException
   #43 = Utf8               (Ljava/lang/Throwable;)V
   #44 = NameAndType        #1:#43        // "<init>":(Ljava/lang/Throwable;)V
   #45 = Methodref          #42.#44       // java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
   #46 = Utf8               m2
   #47 = Utf8               toString
   #48 = Utf8               ()Ljava/lang/String;
   #49 = NameAndType        #46:#10       // m2:Ljava/lang/reflect/Method;
   #50 = Fieldref           #18.#49       // com/sun/proxy/$Proxy0.m2:Ljava/lang/reflect/Method;
   #51 = Utf8               java/lang/String
   #52 = Class              #51           // java/lang/String
   #53 = Utf8               m3
   #54 = Utf8               request
   #55 = Utf8               ()V
   #56 = NameAndType        #53:#10       // m3:Ljava/lang/reflect/Method;
   #57 = Fieldref           #18.#56       // com/sun/proxy/$Proxy0.m3:Ljava/lang/reflect/Method;
   #58 = Utf8               m0
   #59 = Utf8               hashCode
   #60 = Utf8               ()I
   #61 = NameAndType        #58:#10       // m0:Ljava/lang/reflect/Method;
   #62 = Fieldref           #18.#61       // com/sun/proxy/$Proxy0.m0:Ljava/lang/reflect/Method;
   #63 = Utf8               java/lang/Integer
   #64 = Class              #63           // java/lang/Integer
   #65 = Utf8               intValue
   #66 = NameAndType        #65:#60       // intValue:()I
   #67 = Methodref          #64.#66       // java/lang/Integer.intValue:()I
   #68 = Utf8               <clinit>
   #69 = Utf8               java.lang.Object
   #70 = String             #69           // java.lang.Object
   #71 = Utf8               java/lang/Class
   #72 = Class              #71           // java/lang/Class
   #73 = Utf8               forName
   #74 = Utf8               (Ljava/lang/String;)Ljava/lang/Class;
   #75 = NameAndType        #73:#74       // forName:(Ljava/lang/String;)Ljava/lang/Class;
   #76 = Methodref          #72.#75       // java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   #77 = String             #11           // equals
   #78 = Utf8               getMethod
   #79 = Utf8               (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
   #80 = NameAndType        #78:#79       // getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
   #81 = Methodref          #72.#80       // java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Met
hod;
   #82 = String             #47           // toString
   #83 = Utf8               com.compass.spring_lecture.binarycode.dynamicproxy.Subject
   #84 = String             #83           // com.compass.spring_lecture.binarycode.dynamicproxy.Subject
   #85 = String             #54           // request
   #86 = String             #59           // hashCode
   #87 = Utf8               java/lang/NoSuchMethodException
   #88 = Class              #87           // java/lang/NoSuchMethodException
   #89 = Utf8               java/lang/NoSuchMethodError
   #90 = Class              #89           // java/lang/NoSuchMethodError
   #91 = Utf8               getMessage
   #92 = NameAndType        #91:#48       // getMessage:()Ljava/lang/String;
   #93 = Methodref          #40.#92       // java/lang/Throwable.getMessage:()Ljava/lang/String;
   #94 = Utf8               (Ljava/lang/String;)V
   #95 = NameAndType        #1:#94        // "<init>":(Ljava/lang/String;)V
   #96 = Methodref          #90.#95       // java/lang/NoSuchMethodError."<init>":(Ljava/lang/String;)V
   #97 = Utf8               java/lang/ClassNotFoundException
   #98 = Class              #97           // java/lang/ClassNotFoundException
   #99 = Utf8               java/lang/NoClassDefFoundError
  #100 = Class              #99           // java/lang/NoClassDefFoundError
  #101 = Methodref          #100.#95      // java/lang/NoClassDefFoundError."<init>":(Ljava/lang/String;)V
  #102 = Utf8               com/compass/spring_lecture/binarycode/dynamicproxy/Subject
  #103 = Class              #102          // com/compass/spring_lecture/binarycode/dynamicproxy/Subject
{
  public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler) throws ;
    descriptor: (Ljava/lang/reflect/InvocationHandler;)V
    flags: ACC_PUBLIC
    Code:
      stack=10, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokespecial #8                  // Method java/lang/reflect/Proxy."<init>":(Ljava/lang/reflect/InvocationHandler;)V
         5: return
    Exceptions:
      throws

  public final boolean equals(java.lang.Object) throws ;
    descriptor: (Ljava/lang/Object;)Z
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=10, locals=3, args_size=2
         0: aload_0
         1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
         4: aload_0
         5: getstatic     #20                 // Field m1:Ljava/lang/reflect/Method;
         8: iconst_1
         9: anewarray     #22                 // class java/lang/Object
        12: dup
        13: iconst_0
        14: aload_1
        15: aastore
        16: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljav
a/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
        21: checkcast     #30                 // class java/lang/Boolean
        24: invokevirtual #34                 // Method java/lang/Boolean.booleanValue:()Z
        27: ireturn
        28: athrow
        29: astore_2
        30: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
        33: dup
        34: aload_2
        35: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwab
le;)V
        38: athrow
      Exception table:
         from    to  target type
             0    28    28   Class java/lang/Error
             0    28    28   Class java/lang/RuntimeException
             0    28    29   Class java/lang/Throwable
    Exceptions:
      throws

  public final java.lang.String toString() throws ;
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=10, locals=2, args_size=1
         0: aload_0
         1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
         4: aload_0
         5: getstatic     #50                 // Field m2:Ljava/lang/reflect/Method;
         8: aconst_null
         9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljav
a/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
        14: checkcast     #52                 // class java/lang/String
        17: areturn
        18: athrow
        19: astore_1
        20: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
        23: dup
        24: aload_1
        25: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwab
le;)V
        28: athrow
      Exception table:
         from    to  target type
             0    18    18   Class java/lang/Error
             0    18    18   Class java/lang/RuntimeException
             0    18    19   Class java/lang/Throwable
    Exceptions:
      throws

  public final void request() throws ;
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=10, locals=2, args_size=1
         0: aload_0
         1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
         4: aload_0
         5: getstatic     #57                 // Field m3:Ljava/lang/reflect/Method;
         8: aconst_null
         9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljav
a/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
        14: pop
        15: return
        16: athrow
        17: astore_1
        18: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
        21: dup
        22: aload_1
        23: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwab
le;)V
        26: athrow
      Exception table:
         from    to  target type
             0    16    16   Class java/lang/Error
             0    16    16   Class java/lang/RuntimeException
             0    16    17   Class java/lang/Throwable
    Exceptions:
      throws

  public final int hashCode() throws ;
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=10, locals=2, args_size=1
         0: aload_0
         1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
         4: aload_0
         5: getstatic     #62                 // Field m0:Ljava/lang/reflect/Method;
         8: aconst_null
         9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljav
a/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
        14: checkcast     #64                 // class java/lang/Integer
        17: invokevirtual #67                 // Method java/lang/Integer.intValue:()I
        20: ireturn
        21: athrow
        22: astore_1
        23: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
        26: dup
        27: aload_1
        28: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwab
le;)V
        31: athrow
      Exception table:
         from    to  target type
             0    21    21   Class java/lang/Error
             0    21    21   Class java/lang/RuntimeException
             0    21    22   Class java/lang/Throwable
    Exceptions:
      throws

  static {} throws ;
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=10, locals=2, args_size=0
         0: ldc           #70                 // String java.lang.Object
         2: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
         5: ldc           #77                 // String equals
         7: iconst_1
         8: anewarray     #72                 // class java/lang/Class
        11: dup
        12: iconst_0
        13: ldc           #70                 // String java.lang.Object
        15: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
        18: aastore
        19: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/
reflect/Method;
        22: putstatic     #20                 // Field m1:Ljava/lang/reflect/Method;
        25: ldc           #70                 // String java.lang.Object
        27: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
        30: ldc           #82                 // String toString
        32: iconst_0
        33: anewarray     #72                 // class java/lang/Class
        36: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/
reflect/Method;
        39: putstatic     #50                 // Field m2:Ljava/lang/reflect/Method;
        42: ldc           #84                 // String com.compass.spring_lecture.binarycode.dynamicproxy.Subject
        44: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
        47: ldc           #85                 // String request
        49: iconst_0
        50: anewarray     #72                 // class java/lang/Class
        53: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/
reflect/Method;
        56: putstatic     #57                 // Field m3:Ljava/lang/reflect/Method;
        59: ldc           #70                 // String java.lang.Object
        61: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
        64: ldc           #86                 // String hashCode
        66: iconst_0
        67: anewarray     #72                 // class java/lang/Class
        70: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/
reflect/Method;
        73: putstatic     #62                 // Field m0:Ljava/lang/reflect/Method;
        76: return
        77: astore_1
        78: new           #90                 // class java/lang/NoSuchMethodError
        81: dup
        82: aload_1
        83: invokevirtual #93                 // Method java/lang/Throwable.getMessage:()Ljava/lang/String;
        86: invokespecial #96                 // Method java/lang/NoSuchMethodError."<init>":(Ljava/lang/String;)V
        89: athrow
        90: astore_1
        91: new           #100                // class java/lang/NoClassDefFoundError
        94: dup
        95: aload_1
        96: invokevirtual #93                 // Method java/lang/Throwable.getMessage:()Ljava/lang/String;
        99: invokespecial #101                // Method java/lang/NoClassDefFoundError."<init>":(Ljava/lang/String;)V
       102: athrow
      Exception table:
         from    to  target type
             0    77    77   Class java/lang/NoSuchMethodException
             0    77    90   Class java/lang/ClassNotFoundException
    Exceptions:
      throws
}
上一篇下一篇

猜你喜欢

热点阅读