ASM读取class信息

Java asm读取class信息

[TOC]

1 class文件查看方式

1.1 使用jdk提供的javap命令

  • 1.1.1 javap
javap
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置
  • 1.1.2 使用javap命令查看项目中main.jar中的BusIndex.class
javap -v -cp ./main.jar com.eventbus.gen.BusIndex

1.2 使用反编译工具Luyten

  • 1.2.1 用luyten打开main.jar

  • 1.2.2 选项Settings/Bytecode

  • 1.2.3 在Structure中选中BusIndex.class

1.3 反编译结果

javap -v -cp ./main.jar com.eventbus.gen.BusIndex
Classfile jar:file:/D:/projects/Sample/ASMSample/app/libs/main.jar!/com/eventbus/gen/BusIndex.class
  Last modified 2016-10-10; size 1193 bytes
  MD5 checksum 4ac635858d4c2533a29accf9d634f49b
  Compiled from "SourceFile"
public final class com.eventbus.gen.BusIndex extends com.a.a.c.a
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = String             #52            // showMessage
   #2 = String             #53            // showMessagedd
   #3 = Class              #38            // com/a/a/c/a
   #4 = Class              #39            // com/a/a/c/d
   #5 = Class              #40            // com/a/a/d/a
   #6 = Class              #41            // com/eventbus/gen/BusIndex
   #7 = Class              #42            // com/eventbus/gen/a
   #8 = Class              #43            // com/eventbus/gen/b
   #9 = Class              #44            // com/eventbus/gen/c
  #10 = Class              #45            // com/eventbus/gen/d
  #11 = Class              #46            // com/sample/app/Main2Activity
  #12 = Class              #47            // com/sample/app/MainActivity
  #13 = Class              #48            // com/sample/app/MainActivity$a
  #14 = Class              #51            // java/lang/Object
  #15 = Methodref          #3.#23         // com/a/a/c/a."<init>":()V
  #16 = Methodref          #4.#23         // com/a/a/c/d."<init>":()V
  #17 = Methodref          #4.#25         // com/a/a/c/d.a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
  #18 = Methodref          #7.#24         // com/eventbus/gen/a."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #19 = Methodref          #8.#24         // com/eventbus/gen/b."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #20 = Methodref          #9.#24         // com/eventbus/gen/c."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #21 = Methodref          #10.#24        // com/eventbus/gen/d."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #22 = Methodref          #14.#26        // java/lang/Object.getClass:()Ljava/lang/Class;
  #23 = NameAndType        #32:#28        // "<init>":()V
  #24 = NameAndType        #32:#29        // "<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #25 = NameAndType        #37:#31        // a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
  #26 = NameAndType        #50:#27        // getClass:()Ljava/lang/Class;
  #27 = Utf8               ()Ljava/lang/Class;
  #28 = Utf8               ()V
  #29 = Utf8               (Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
  #30 = Utf8               (Ljava/lang/Object;)Lcom/a/a/c/d;
  #31 = Utf8               (Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
  #32 = Utf8               <init>
  #33 = Utf8               Code
  #34 = Utf8               InnerClasses
  #35 = Utf8               LineNumberTable
  #36 = Utf8               SourceFile
  #37 = Utf8               a
  #38 = Utf8               com/a/a/c/a
  #39 = Utf8               com/a/a/c/d
  #40 = Utf8               com/a/a/d/a
  #41 = Utf8               com/eventbus/gen/BusIndex
  #42 = Utf8               com/eventbus/gen/a
  #43 = Utf8               com/eventbus/gen/b
  #44 = Utf8               com/eventbus/gen/c
  #45 = Utf8               com/eventbus/gen/d
  #46 = Utf8               com/sample/app/Main2Activity
  #47 = Utf8               com/sample/app/MainActivity
  #48 = Utf8               com/sample/app/MainActivity$a
  #49 = Utf8               generateSubscribeMethodIndex
  #50 = Utf8               getClass
  #51 = Utf8               java/lang/Object
  #52 = Utf8               showMessage
  #53 = Utf8               showMessagedd
{
  public com.eventbus.gen.BusIndex();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #15                 // Method com/a/a/c/a."<init>":()V
         4: return
      LineNumberTable:
        line 15: 0

  public com.a.a.c.d generateSubscribeMethodIndex(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Lcom/a/a/c/d;
    flags: ACC_PUBLIC
    Code:
      stack=11, locals=7, args_size=2
         0: new           #4                  // class com/a/a/c/d
         3: dup
         4: invokespecial #16                 // Method com/a/a/c/d."<init>":()V
         7: astore_2
         8: aload_1
         9: invokevirtual #22                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
        12: astore_3
        13: aconst_null
        14: astore        4
        16: aconst_null
        17: astore        5
        19: aconst_null
        20: astore        6
        22: aload_3
        23: ldc           #12                 // class com/sample/app/MainActivity
        25: if_acmpne     104
        28: ldc           #13                 // class com/sample/app/MainActivity$a
        30: astore        4
        32: ldc           #5                  // class com/a/a/d/a
        34: astore        5
        36: ldc           #1                  // String showMessage
        38: astore        6
        40: aload_2
        41: aload         6
        43: aload         4
        45: aload         5
        47: new           #7                  // class com/eventbus/gen/a
        50: dup
        51: aload_0
        52: aload_1
        53: aload         6
        55: aload         4
        57: aload         5
        59: invokespecial #18                 // Method com/eventbus/gen/a."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
        62: invokevirtual #17                 // Method com/a/a/c/d.a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
        65: ldc           #13                 // class com/sample/app/MainActivity$a
        67: astore        4
        69: ldc           #5                  // class com/a/a/d/a
        71: astore        5
        73: ldc           #2                  // String showMessagedd
        75: astore        6
        77: aload_2
        78: aload         6
        80: aload         4
        82: aload         5
        84: new           #8                  // class com/eventbus/gen/b
        87: dup
        88: aload_0
        89: aload_1
        90: aload         6
        92: aload         4
        94: aload         5
        96: invokespecial #19                 // Method com/eventbus/gen/b."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
        99: invokevirtual #17                 // Method com/a/a/c/d.a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
       102: aload_2
       103: areturn
       104: aload_3
       105: ldc           #11                 // class com/sample/app/Main2Activity
       107: if_acmpne     186
       110: ldc           #13                 // class com/sample/app/MainActivity$a
       112: astore        4
       114: ldc           #5                  // class com/a/a/d/a
       116: astore        5
       118: ldc           #1                  // String showMessage
       120: astore        6
       122: aload_2
       123: aload         6
       125: aload         4
       127: aload         5
       129: new           #9                  // class com/eventbus/gen/c
       132: dup
       133: aload_0
       134: aload_1
       135: aload         6
       137: aload         4
       139: aload         5
       141: invokespecial #20                 // Method com/eventbus/gen/c."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
       144: invokevirtual #17                 // Method com/a/a/c/d.a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
       147: ldc           #13                 // class com/sample/app/MainActivity$a
       149: astore        4
       151: ldc           #5                  // class com/a/a/d/a
       153: astore        5
       155: ldc           #2                  // String showMessagedd
       157: astore        6
       159: aload_2
       160: aload         6
       162: aload         4
       164: aload         5
       166: new           #10                 // class com/eventbus/gen/d
       169: dup
       170: aload_0
       171: aload_1
       172: aload         6
       174: aload         4
       176: aload         5
       178: invokespecial #21                 // Method com/eventbus/gen/d."<init>":(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
       181: invokevirtual #17                 // Method com/a/a/c/d.a:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
       184: aload_2
       185: areturn
       186: aload_2
       187: areturn
      LineNumberTable:
        line 18: 0
        line 19: 8
        line 20: 13
        line 21: 16
        line 22: 19
        line 23: 22
        line 24: 28
        line 25: 32
        line 26: 36
        line 27: 40
        line 33: 65
        line 34: 69
        line 35: 73
        line 36: 77
        line 42: 102
        line 44: 104
        line 45: 110
        line 46: 114
        line 47: 118
        line 48: 122
        line 54: 147
        line 55: 151
        line 56: 155
        line 57: 159
        line 63: 184
        line 65: 186
}
InnerClasses:
     #10; //class com/eventbus/gen/d
     #9; //class com/eventbus/gen/c
     #8; //class com/eventbus/gen/b
     #7; //class com/eventbus/gen/a
     public static #37= #13 of #12; //a=class com/sample/app/MainActivity$a of class com/sample/app/MainActivity
SourceFile: "SourceFile"

2 asm中与class中的指令和类型映射


2.1 Opcodes中的指令

public interface Opcodes {

    // ASM API versions

    int ASM4 = 4 << 16 | 0 << 8 | 0;
    int ASM5 = 5 << 16 | 0 << 8 | 0;
    int ASM6 = 6 << 16 | 0 << 8 | 0;

    // versions

    int V1_1 = 3 << 16 | 45;
    int V1_2 = 0 << 16 | 46;
    int V1_3 = 0 << 16 | 47;
    int V1_4 = 0 << 16 | 48;
    int V1_5 = 0 << 16 | 49;
    int V1_6 = 0 << 16 | 50;
    int V1_7 = 0 << 16 | 51;
    int V1_8 = 0 << 16 | 52;
    int V1_9 = 0 << 16 | 53;

    // access flags

    int ACC_PUBLIC = 0x0001; // class, field, method
    int ACC_PRIVATE = 0x0002; // class, field, method
    int ACC_PROTECTED = 0x0004; // class, field, method
    int ACC_STATIC = 0x0008; // field, method
    int ACC_FINAL = 0x0010; // class, field, method, parameter
    int ACC_SUPER = 0x0020; // class
    int ACC_SYNCHRONIZED = 0x0020; // method
    int ACC_VOLATILE = 0x0040; // field
    int ACC_BRIDGE = 0x0040; // method
    int ACC_VARARGS = 0x0080; // method
    int ACC_TRANSIENT = 0x0080; // field
    int ACC_NATIVE = 0x0100; // method
    int ACC_INTERFACE = 0x0200; // class
    int ACC_ABSTRACT = 0x0400; // class, method
    int ACC_STRICT = 0x0800; // method
    int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter
    int ACC_ANNOTATION = 0x2000; // class
    int ACC_ENUM = 0x4000; // class(?) field inner
    int ACC_MANDATED = 0x8000; // parameter
    int ACC_MODULE = 0x8000; // class

    // ASM specific pseudo access flags

    int ACC_DEPRECATED = 0x20000; // class, field, method

    // types for NEWARRAY

    int T_BOOLEAN = 4;
    int T_CHAR = 5;
    int T_FLOAT = 6;
    int T_DOUBLE = 7;
    int T_BYTE = 8;
    int T_SHORT = 9;
    int T_INT = 10;
    int T_LONG = 11;

    // tags for Handle

    int H_GETFIELD = 1;
    int H_GETSTATIC = 2;
    int H_PUTFIELD = 3;
    int H_PUTSTATIC = 4;
    int H_INVOKEVIRTUAL = 5;
    int H_INVOKESTATIC = 6;
    int H_INVOKESPECIAL = 7;
    int H_NEWINVOKESPECIAL = 8;
    int H_INVOKEINTERFACE = 9;

    // stack map frame types

    /**
     * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}.
     */
    int F_NEW = -1;

    /**
     * Represents a compressed frame with complete frame data.
     */
    int F_FULL = 0;

    /**
     * Represents a compressed frame where locals are the same as the locals in
     * the previous frame, except that additional 1-3 locals are defined, and
     * with an empty stack.
     */
    int F_APPEND = 1;

    /**
     * Represents a compressed frame where locals are the same as the locals in
     * the previous frame, except that the last 1-3 locals are absent and with
     * an empty stack.
     */
    int F_CHOP = 2;

    /**
     * Represents a compressed frame with exactly the same locals as the
     * previous frame and with an empty stack.
     */
    int F_SAME = 3;

    /**
     * Represents a compressed frame with exactly the same locals as the
     * previous frame and with a single value on the stack.
     */
    int F_SAME1 = 4;

    // Do not try to change the following code to use auto-boxing,
    // these values are compared by reference and not by value
    // The constructor of Integer was deprecated in 9
    // but we are stuck with it by backward compatibility
    @SuppressWarnings("deprecation") Integer TOP = new Integer(0);
    @SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
    @SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
    @SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
    @SuppressWarnings("deprecation") Integer LONG = new Integer(4);
    @SuppressWarnings("deprecation") Integer NULL = new Integer(5);
    @SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6);

    // opcodes // visit method (- = idem)

    int NOP = 0; // visitInsn
    int ACONST_NULL = 1; // -
    int ICONST_M1 = 2; // -
    int ICONST_0 = 3; // -
    int ICONST_1 = 4; // -
    int ICONST_2 = 5; // -
    int ICONST_3 = 6; // -
    int ICONST_4 = 7; // -
    int ICONST_5 = 8; // -
    int LCONST_0 = 9; // -
    int LCONST_1 = 10; // -
    int FCONST_0 = 11; // -
    int FCONST_1 = 12; // -
    int FCONST_2 = 13; // -
    int DCONST_0 = 14; // -
    int DCONST_1 = 15; // -
    int BIPUSH = 16; // visitIntInsn
    int SIPUSH = 17; // -
    int LDC = 18; // visitLdcInsn
    // int LDC_W = 19; // -
    // int LDC2_W = 20; // -
    int ILOAD = 21; // visitVarInsn
    int LLOAD = 22; // -
    int FLOAD = 23; // -
    int DLOAD = 24; // -
    int ALOAD = 25; // -
    // int ILOAD_0 = 26; // -
    // int ILOAD_1 = 27; // -
    // int ILOAD_2 = 28; // -
    // int ILOAD_3 = 29; // -
    // int LLOAD_0 = 30; // -
    // int LLOAD_1 = 31; // -
    // int LLOAD_2 = 32; // -
    // int LLOAD_3 = 33; // -
    // int FLOAD_0 = 34; // -
    // int FLOAD_1 = 35; // -
    // int FLOAD_2 = 36; // -
    // int FLOAD_3 = 37; // -
    // int DLOAD_0 = 38; // -
    // int DLOAD_1 = 39; // -
    // int DLOAD_2 = 40; // -
    // int DLOAD_3 = 41; // -
    // int ALOAD_0 = 42; // -
    // int ALOAD_1 = 43; // -
    // int ALOAD_2 = 44; // -
    // int ALOAD_3 = 45; // -
    int IALOAD = 46; // visitInsn
    int LALOAD = 47; // -
    int FALOAD = 48; // -
    int DALOAD = 49; // -
    int AALOAD = 50; // -
    int BALOAD = 51; // -
    int CALOAD = 52; // -
    int SALOAD = 53; // -
    int ISTORE = 54; // visitVarInsn
    int LSTORE = 55; // -
    int FSTORE = 56; // -
    int DSTORE = 57; // -
    int ASTORE = 58; // -
    // int ISTORE_0 = 59; // -
    // int ISTORE_1 = 60; // -
    // int ISTORE_2 = 61; // -
    // int ISTORE_3 = 62; // -
    // int LSTORE_0 = 63; // -
    // int LSTORE_1 = 64; // -
    // int LSTORE_2 = 65; // -
    // int LSTORE_3 = 66; // -
    // int FSTORE_0 = 67; // -
    // int FSTORE_1 = 68; // -
    // int FSTORE_2 = 69; // -
    // int FSTORE_3 = 70; // -
    // int DSTORE_0 = 71; // -
    // int DSTORE_1 = 72; // -
    // int DSTORE_2 = 73; // -
    // int DSTORE_3 = 74; // -
    // int ASTORE_0 = 75; // -
    // int ASTORE_1 = 76; // -
    // int ASTORE_2 = 77; // -
    // int ASTORE_3 = 78; // -
    int IASTORE = 79; // visitInsn
    int LASTORE = 80; // -
    int FASTORE = 81; // -
    int DASTORE = 82; // -
    int AASTORE = 83; // -
    int BASTORE = 84; // -
    int CASTORE = 85; // -
    int SASTORE = 86; // -
    int POP = 87; // -
    int POP2 = 88; // -
    int DUP = 89; // -
    int DUP_X1 = 90; // -
    int DUP_X2 = 91; // -
    int DUP2 = 92; // -
    int DUP2_X1 = 93; // -
    int DUP2_X2 = 94; // -
    int SWAP = 95; // -
    int IADD = 96; // -
    int LADD = 97; // -
    int FADD = 98; // -
    int DADD = 99; // -
    int ISUB = 100; // -
    int LSUB = 101; // -
    int FSUB = 102; // -
    int DSUB = 103; // -
    int IMUL = 104; // -
    int LMUL = 105; // -
    int FMUL = 106; // -
    int DMUL = 107; // -
    int IDIV = 108; // -
    int LDIV = 109; // -
    int FDIV = 110; // -
    int DDIV = 111; // -
    int IREM = 112; // -
    int LREM = 113; // -
    int FREM = 114; // -
    int DREM = 115; // -
    int INEG = 116; // -
    int LNEG = 117; // -
    int FNEG = 118; // -
    int DNEG = 119; // -
    int ISHL = 120; // -
    int LSHL = 121; // -
    int ISHR = 122; // -
    int LSHR = 123; // -
    int IUSHR = 124; // -
    int LUSHR = 125; // -
    int IAND = 126; // -
    int LAND = 127; // -
    int IOR = 128; // -
    int LOR = 129; // -
    int IXOR = 130; // -
    int LXOR = 131; // -
    int IINC = 132; // visitIincInsn
    int I2L = 133; // visitInsn
    int I2F = 134; // -
    int I2D = 135; // -
    int L2I = 136; // -
    int L2F = 137; // -
    int L2D = 138; // -
    int F2I = 139; // -
    int F2L = 140; // -
    int F2D = 141; // -
    int D2I = 142; // -
    int D2L = 143; // -
    int D2F = 144; // -
    int I2B = 145; // -
    int I2C = 146; // -
    int I2S = 147; // -
    int LCMP = 148; // -
    int FCMPL = 149; // -
    int FCMPG = 150; // -
    int DCMPL = 151; // -
    int DCMPG = 152; // -
    int IFEQ = 153; // visitJumpInsn
    int IFNE = 154; // -
    int IFLT = 155; // -
    int IFGE = 156; // -
    int IFGT = 157; // -
    int IFLE = 158; // -
    int IF_ICMPEQ = 159; // -
    int IF_ICMPNE = 160; // -
    int IF_ICMPLT = 161; // -
    int IF_ICMPGE = 162; // -
    int IF_ICMPGT = 163; // -
    int IF_ICMPLE = 164; // -
    int IF_ACMPEQ = 165; // -
    int IF_ACMPNE = 166; // -
    int GOTO = 167; // -
    int JSR = 168; // -
    int RET = 169; // visitVarInsn
    int TABLESWITCH = 170; // visiTableSwitchInsn
    int LOOKUPSWITCH = 171; // visitLookupSwitch
    int IRETURN = 172; // visitInsn
    int LRETURN = 173; // -
    int FRETURN = 174; // -
    int DRETURN = 175; // -
    int ARETURN = 176; // -
    int RETURN = 177; // -
    int GETSTATIC = 178; // visitFieldInsn
    int PUTSTATIC = 179; // -
    int GETFIELD = 180; // -
    int PUTFIELD = 181; // -
    int INVOKEVIRTUAL = 182; // visitMethodInsn
    int INVOKESPECIAL = 183; // -
    int INVOKESTATIC = 184; // -
    int INVOKEINTERFACE = 185; // -
    int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
    int NEW = 187; // visitTypeInsn
    int NEWARRAY = 188; // visitIntInsn
    int ANEWARRAY = 189; // visitTypeInsn
    int ARRAYLENGTH = 190; // visitInsn
    int ATHROW = 191; // -
    int CHECKCAST = 192; // visitTypeInsn
    int INSTANCEOF = 193; // -
    int MONITORENTER = 194; // visitInsn
    int MONITOREXIT = 195; // -
    // int WIDE = 196; // NOT VISITED
    int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
    int IFNULL = 198; // visitJumpInsn
    int IFNONNULL = 199; // -
    // int GOTO_W = 200; // -
    // int JSR_W = 201; // -
}

3 asm中使用ClassReader读取.class信息

3.1 ClassReader提供四个构造方法用来加载class

public ClassReader(final byte[] b)
public ClassReader(final InputStream is)
public ClassReader(final String name)
public ClassReader(final byte[] b, final int off, final int len)

3.2 读取BusIndex.class基本信息

  • 3.2.1 加载class
ClassReader classReader = new ClassReader(BusIndex.class.getName());
  • 3.2.2 获取类名
classReader.getClassName()
  • 3.2.3 获取父类名
classReader.getSuperName()
  • 3.2.4 获取接口
classReader.getInterfaces()

3.3 打印class基本信息

System.out.println(classReader.getClassName());
System.out.println(Arrays.toString(classReader.getInterfaces()));
System.out.println(classReader.getSuperName());

3.4 控制台输出结果

com/eventbus/gen/BusIndex
[]
com/a/a/c/a

3.5 使用ClassVisitor读取BusIndex.class详细信息

  • 3.5.1 ClassReader提供了 accept方法可以遍历class的具体信息,该方法有两个重载
public void accept(final ClassVisitor classVisitor,final Attribute[] attrs, final int flags)
public void accept(final ClassVisitor classVisitor,final int flags)
  • 3.5.2 其中参数ClassVisitor用来遍历class信息, flags用来设置遍历信息级别

  • 3.5.3 遍历class具体信息

classReader.accept(new SimpleClassVisitor(), 0);
static class SimpleClassVisitor extends ClassVisitor {

        public SimpleClassVisitor() {
            super(Opcodes.ASM6);
        }

        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", version:"+version+", access:"+access+", name:"+name+", signature:"+signature+", supperName:"+signature+", interfaces:"+ Arrays.toString(interfaces));

        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", desc:"+desc+", visible:"+visible);

            return super.visitAnnotation(desc, visible);
        }

        @Override
        public void visitAttribute(Attribute attr) {
            super.visitAttribute(attr);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", attr:"+attr);

        }

        @Override
        public void visitEnd() {
            super.visitEnd();
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());

        }

        @Override
        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", access:"+access+", name:"+name+", desc:"+desc+", signature:"+signature+", value:"+value);

            return super.visitField(access, name, desc, signature, value);
        }

        @Override
        public void visitInnerClass(String name, String outerName, String innerName, int access) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", name:"+name+", outerName:"+outerName+", innerName:"+innerName+", access:"+access);

            super.visitInnerClass(name, outerName, innerName, access);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", access:"+access+", name:"+name+", desc:"+desc+", signature:"+signature+", exceptions:"+Arrays.toString(exceptions));


            return super.visitMethod(access, name, desc, signature, exceptions);
        }

        @Override
        public ModuleVisitor visitModule() {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());

            return super.visitModule();
        }

        @Override
        public void visitOuterClass(String owner, String name, String desc) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", owner:"+owner+", name:"+name+", desc:"+desc);

            super.visitOuterClass(owner, name, desc);
        }

        @Override
        public void visitSource(String source, String debug) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", source:"+source+", debug:"+debug);

            super.visitSource(source, debug);
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", typeRef:"+typeRef+", typePath:"+typePath+", desc:"+desc+", visible:"+visible);

            return super.visitTypeAnnotation(typeRef, typePath, desc, visible);
        }
    }
  • 3.5.4 控制台输出结果
visit, version:51, access:49, name:com/eventbus/gen/BusIndex, signature:null, supperName:null, interfaces:[]
visitSource, source:SourceFile, debug:null
visitInnerClass, name:com/eventbus/gen/d, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/c, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/b, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/a, outerName:null, innerName:null, access:0
visitInnerClass, name:com/sample/app/MainActivity$a, outerName:com/sample/app/MainActivity, innerName:a, access:9
visitMethod, access:1, name:<init>, desc:()V, signature:null, exceptions:null
visitMethod, access:1, name:generateSubscribeMethodIndex, desc:(Ljava/lang/Object;)Lcom/a/a/c/d;, signature:null, exceptions:null
visitEnd
  • 3.5.5 从输入结果中可以看出

    • version:

    version:51 与反编译结果一致

    • flags:

    access:49 与反编译结果一致Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_SUPER = 49

    • InnerClasses:

    visitInnerClass, name:com/eventbus/gen/d, outerName:null, innerName:null, access:0 visitInnerClass, name:com/eventbus/gen/c, outerName:null, innerName:null, access:0 visitInnerClass, name:com/eventbus/gen/b, outerName:null, innerName:null, access:0 visitInnerClass, name:com/eventbus/gen/a, outerName:null, innerName:null, access:0 visitInnerClass, name:com/sample/app/MainActivity$a, outerName:com/sample/app/MainActivity, innerName:a, access:9

    • method:

    visitMethod, access:1, name:<init>, desc:()V, signature:null, exceptions:null visitMethod, access:1, name:generateSubscribeMethodIndex, desc:(Ljava/lang/Object;)Lcom/a/a/c/d;, signature:null, exceptions:null

3.6 使用MethodVisitor读取BusIndex.class中方法的详细信息

  • 3.6.1 ClassVisitor的visitMethod只能获取方法的基本信息,如果要获取方法的详细信息就必须在visitMethod返回一个自定义的MethodVisitor。
static class SimpleMethodVisitor extends MethodVisitor{
        public SimpleMethodVisitor(MethodVisitor mv) {
            super(Opcodes.ASM6, mv);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", desc:"+desc+", visible:"+visible);

            return super.visitAnnotation(desc, visible);
        }

        @Override
        public AnnotationVisitor visitAnnotationDefault() {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());

            return super.visitAnnotationDefault();
        }

        @Override
        public void visitAttribute(Attribute attr) {
            super.visitAttribute(attr);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", attr:"+attr);
        }

        @Override
        public void visitCode() {
            super.visitCode();
            System.out.println("visitor method begin.");
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());

        }

        @Override
        public void visitEnd() {
            super.visitEnd();
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());
            System.out.println("visitor method ended.");
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            super.visitFieldInsn(opcode, owner, name, desc);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+Optcodes.options.get(opcode)+", owner:"+owner+", name:"+name+", desc:"+desc);

        }

        @Override
        public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
            super.visitFrame(type, nLocal, local, nStack, stack);

        }

        @Override
        public void visitIincInsn(int var, int increment) {
            super.visitIincInsn(var, increment);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", var:"+var+", increment:"+increment);

        }

        @Override
        public void visitInsn(int opcode) {
            super.visitInsn(opcode);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+Optcodes.options.get(opcode));

        }

        @Override
        public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", typeRef:"+typeRef+", typePath:"+typePath+", desc:"+desc+", visible:"+visible);

            return super.visitInsnAnnotation(typeRef, typePath, desc, visible);
        }

        @Override
        public void visitIntInsn(int opcode, int operand) {
            super.visitIntInsn(opcode, operand);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+Optcodes.options.get(opcode)+", operand:"+operand);

        }

        @Override
        public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
            super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
        }

        @Override
        public void visitJumpInsn(int opcode, Label label) {
            super.visitJumpInsn(opcode, label);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+Optcodes.options.get(opcode)+", label:"+label);

        }

        @Override
        public void visitLabel(Label label) {
            super.visitLabel(label);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", label:"+label);

        }

        @Override
        public void visitLdcInsn(Object cst) {
            super.visitLdcInsn(cst);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", cst:"+cst);

        }

        @Override
        public void visitLineNumber(int line, Label start) {
            super.visitLineNumber(line, start);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", line:"+line+", start label:"+line);
        }

        @Override
        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
            super.visitLocalVariable(name, desc, signature, start, end, index);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", name:"+name+", desc:"+desc+", signature:"+signature);

        }

        @Override
        public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) {
            return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible);
        }

        @Override
        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
            super.visitLookupSwitchInsn(dflt, keys, labels);
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            super.visitMaxs(maxStack, maxLocals);
        }



        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            super.visitMethodInsn(opcode, owner, name, desc, itf);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+Optcodes.options.get(opcode)+", owner:"+owner+", name:"+name+", desc:"+desc);

        }

        @Override
        public void visitMultiANewArrayInsn(String desc, int dims) {
            super.visitMultiANewArrayInsn(desc, dims);
        }

        @Override
        public void visitParameter(String name, int access) {
            super.visitParameter(name, access);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", name:"+name+", access:"+access);

        }

        @Override
        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
            return super.visitParameterAnnotation(parameter, desc, visible);
        }

        @Override
        public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
            super.visitTableSwitchInsn(min, max, dflt, labels);
        }

        @Override
        public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            return super.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
        }

        @Override
        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
            super.visitTryCatchBlock(start, end, handler, type);
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            return super.visitTypeAnnotation(typeRef, typePath, desc, visible);

        }

        @Override
        public void visitTypeInsn(int opcode, String type) {
            super.visitTypeInsn(opcode, type);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+ Optcodes.options.get(opcode)+", type:"+type);

        }

        @Override
        public void visitVarInsn(int opcode, int var) {
            super.visitVarInsn(opcode, var);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", opcode:"+ Optcodes.options.get(opcode)+",var:"+var);

        }
    }
  • 3.6.2 在ClassVisitor的visitMethod方法中使用SimpleMethodVisitor获取generateSubscribeMethodIndex方法的具体信息
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
            System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+", access:"+access+", name:"+name+", desc:"+desc+", signature:"+signature+", exceptions:"+Arrays.toString(exceptions));
            if(name.equals("generateSubscribeMethodIndex")){
                return new SimpleMethodVisitor(methodVisitor);
            }
            return methodVisitor;
        }
  • 3.6.3 输出结果
visit, version:51, access:49, name:com/eventbus/gen/BusIndex, signature:null, supperName:null, interfaces:[]
visitSource, source:SourceFile, debug:null
visitInnerClass, name:com/eventbus/gen/d, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/c, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/b, outerName:null, innerName:null, access:0
visitInnerClass, name:com/eventbus/gen/a, outerName:null, innerName:null, access:0
visitInnerClass, name:com/sample/app/MainActivity$a, outerName:com/sample/app/MainActivity, innerName:a, access:9
visitMethod, access:1, name:<init>, desc:()V, signature:null, exceptions:null
visitMethod, access:1, name:generateSubscribeMethodIndex, desc:(Ljava/lang/Object;)Lcom/a/a/c/d;, signature:null, exceptions:null
visitor method begin.
visitCode
visitLabel, label:L1072591677
visitLineNumber, line:18, start label:18
visitTypeInsn, opcode:NEW, type:com/a/a/c/d
visitInsn, opcode:DUP
visitMethodInsn, opcode:INVOKESPECIAL, owner:com/a/a/c/d, name:<init>, desc:()V
visitVarInsn, opcode:ASTORE,var:2
visitLabel, label:L918221580
visitLineNumber, line:19, start label:19
visitVarInsn, opcode:ALOAD,var:1
visitMethodInsn, opcode:INVOKEVIRTUAL, owner:java/lang/Object, name:getClass, desc:()Ljava/lang/Class;
visitVarInsn, opcode:ASTORE,var:3
visitLabel, label:L2055281021
visitLineNumber, line:20, start label:20
visitInsn, opcode:ACONST_NULL
visitVarInsn, opcode:ASTORE,var:4
visitLabel, label:L1554547125
visitLineNumber, line:21, start label:21
visitInsn, opcode:ACONST_NULL
visitVarInsn, opcode:ASTORE,var:5
visitLabel, label:L617901222
visitLineNumber, line:22, start label:22
visitInsn, opcode:ACONST_NULL
visitVarInsn, opcode:ASTORE,var:6
visitLabel, label:L1159190947
visitLineNumber, line:23, start label:23
visitVarInsn, opcode:ALOAD,var:3
visitLdcInsn, cst:Lcom/sample/app/MainActivity;
visitJumpInsn, opcode:IF_ACMPNE, label:L681842940
visitLabel, label:L1392838282
visitLineNumber, line:24, start label:24
visitLdcInsn, cst:Lcom/sample/app/MainActivity$a;
visitVarInsn, opcode:ASTORE,var:4
visitLabel, label:L523429237
visitLineNumber, line:25, start label:25
visitLdcInsn, cst:Lcom/a/a/d/a;
visitVarInsn, opcode:ASTORE,var:5
visitLabel, label:L664740647
visitLineNumber, line:26, start label:26
visitLdcInsn, cst:showMessage
visitVarInsn, opcode:ASTORE,var:6
visitLabel, label:L804564176
visitLineNumber, line:27, start label:27
visitVarInsn, opcode:ALOAD,var:2
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitTypeInsn, opcode:NEW, type:com/eventbus/gen/a
visitInsn, opcode:DUP
visitVarInsn, opcode:ALOAD,var:0
visitVarInsn, opcode:ALOAD,var:1
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitMethodInsn, opcode:INVOKESPECIAL, owner:com/eventbus/gen/a, name:<init>, desc:(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
visitMethodInsn, opcode:INVOKEVIRTUAL, owner:com/a/a/c/d, name:a, desc:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
visitLabel, label:L1421795058
visitLineNumber, line:33, start label:33
visitLdcInsn, cst:Lcom/sample/app/MainActivity$a;
visitVarInsn, opcode:ASTORE,var:4
visitLabel, label:L1555009629
visitLineNumber, line:34, start label:34
visitLdcInsn, cst:Lcom/a/a/d/a;
visitVarInsn, opcode:ASTORE,var:5
visitLabel, label:L41359092
visitLineNumber, line:35, start label:35
visitLdcInsn, cst:showMessagedd
visitVarInsn, opcode:ASTORE,var:6
visitLabel, label:L149928006
visitLineNumber, line:36, start label:36
visitVarInsn, opcode:ALOAD,var:2
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitTypeInsn, opcode:NEW, type:com/eventbus/gen/b
visitInsn, opcode:DUP
visitVarInsn, opcode:ALOAD,var:0
visitVarInsn, opcode:ALOAD,var:1
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitMethodInsn, opcode:INVOKESPECIAL, owner:com/eventbus/gen/b, name:<init>, desc:(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
visitMethodInsn, opcode:INVOKEVIRTUAL, owner:com/a/a/c/d, name:a, desc:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
visitLabel, label:L713338599
visitLineNumber, line:42, start label:42
visitVarInsn, opcode:ALOAD,var:2
visitInsn, opcode:ARETURN
visitLabel, label:L681842940
visitLineNumber, line:44, start label:44
visitVarInsn, opcode:ALOAD,var:3
visitLdcInsn, cst:Lcom/sample/app/Main2Activity;
visitJumpInsn, opcode:IF_ACMPNE, label:L168423058
visitLabel, label:L821270929
visitLineNumber, line:45, start label:45
visitLdcInsn, cst:Lcom/sample/app/MainActivity$a;
visitVarInsn, opcode:ASTORE,var:4
visitLabel, label:L1160460865
visitLineNumber, line:46, start label:46
visitLdcInsn, cst:Lcom/a/a/d/a;
visitVarInsn, opcode:ASTORE,var:5
visitLabel, label:L1247233941
visitLineNumber, line:47, start label:47
visitLdcInsn, cst:showMessage
visitVarInsn, opcode:ASTORE,var:6
visitLabel, label:L258952499
visitLineNumber, line:48, start label:48
visitVarInsn, opcode:ALOAD,var:2
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitTypeInsn, opcode:NEW, type:com/eventbus/gen/c
visitInsn, opcode:DUP
visitVarInsn, opcode:ALOAD,var:0
visitVarInsn, opcode:ALOAD,var:1
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitMethodInsn, opcode:INVOKESPECIAL, owner:com/eventbus/gen/c, name:<init>, desc:(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
visitMethodInsn, opcode:INVOKEVIRTUAL, owner:com/a/a/c/d, name:a, desc:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
visitLabel, label:L603742814
visitLineNumber, line:54, start label:54
visitLdcInsn, cst:Lcom/sample/app/MainActivity$a;
visitVarInsn, opcode:ASTORE,var:4
visitLabel, label:L1067040082
visitLineNumber, line:55, start label:55
visitLdcInsn, cst:Lcom/a/a/d/a;
visitVarInsn, opcode:ASTORE,var:5
visitLabel, label:L1325547227
visitLineNumber, line:56, start label:56
visitLdcInsn, cst:showMessagedd
visitVarInsn, opcode:ASTORE,var:6
visitLabel, label:L980546781
visitLineNumber, line:57, start label:57
visitVarInsn, opcode:ALOAD,var:2
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitTypeInsn, opcode:NEW, type:com/eventbus/gen/d
visitInsn, opcode:DUP
visitVarInsn, opcode:ALOAD,var:0
visitVarInsn, opcode:ALOAD,var:1
visitVarInsn, opcode:ALOAD,var:6
visitVarInsn, opcode:ALOAD,var:4
visitVarInsn, opcode:ALOAD,var:5
visitMethodInsn, opcode:INVOKESPECIAL, owner:com/eventbus/gen/d, name:<init>, desc:(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V
visitMethodInsn, opcode:INVOKEVIRTUAL, owner:com/a/a/c/d, name:a, desc:(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V
visitLabel, label:L2061475679
visitLineNumber, line:63, start label:63
visitVarInsn, opcode:ALOAD,var:2
visitInsn, opcode:ARETURN
visitLabel, label:L168423058
visitLineNumber, line:65, start label:65
visitVarInsn, opcode:ALOAD,var:2
visitInsn, opcode:ARETURN
visitEnd
visitor method ended.
visitEnd
  • 3.6.4 从结果中可以看出visitor method begin与visitor method ended之间输出的就是generateSubscribeMethodIndex的具体信息

3.7 同理如果要读取字段,枚举,变量等的具体信息在相应的方法中返回自定义的visitor即可

转载于:https://my.oschina.net/jackyanngo/blog/776779

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值