us apple developer accounts for sale:class文件的基本结构及proxy源码剖析二

admin 4个月前 (07-11) 科技 45 0

前文地址:https://www.cnblogs.com/tera/p/13267630.html

本系列文章主要是博主在学习spring aop的过程中领会到其使用了java动态署理,本着究根问底的态度,于是对java动态署理的本质原理做了一些研究,于是便有了这个系列的文章

 

接上文,我们需要领会class字节码的结构,才气更好地明白后面的代码,这里我直接引用jvm文档中的内容

jvm文档地址:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
下面临字节码的结构简朴地做了个说明,大部门都是顾名思义

ClassFile {
    u4             magic;//牢固的开头,值为0xCAFEBABE
    u2             minor_version;//版本号,用来符号class的版本
    u2             major_version;//版本号,用来符号class的版本
    u2             constant_pool_count;//静态池巨细,是静态池工具数目+1
    cp_info        constant_pool[constant_pool_count-1];//静态池工具,有用索引是1 ~ count-1
    u2             access_flags;//public、final等形貌
    u2             this_class;//当前类的信息
    u2             super_class;//父类的信息
    u2             interfaces_count;//接口数目
    u2             interfaces[interfaces_count];//接口工具
    u2             fields_count;//字段数目
    field_info     fields[fields_count];//字段工具
    u2             methods_count;//方式数目
    method_info    methods[methods_count];//方式工具
    u2             attributes_count;//属性数目
    attribute_info attributes[attributes_count];//属性工具
}

为了不成为一篇死板的文档翻译,而且尽快进入Proxy的源码,这里并不会对每一个部门做稀奇详细的说明,以掌握整体为主

回忆上篇文章最后,源码我们看到了这里

ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();

接下去我们就可以进入generateClassFile()方式了

掌握整体,我们先跳过一部门细节代码,先看下面这部门(这里我做了一个可读性的变量名修改)

注重对照着Class的字节结构来看

最终输出的字节省

ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream data = new DataOutputStream(byteStream);

写入牢固开头magic,这里-889275714就是对应0xCAFEBABE

data.writeInt(-889275714);

写入版本号

data.writeShort(0);//minor_version
data.writeShort(49);//major_version

写入常量池,这里cp就是指constant pool

this.cp.write(data);

这里我们需要进入cp的write方式看一下,也先不要纠结Entry的细节,我们照样先掌握整体

public void write(OutputStream var1) throws IOException {
DataOutputStream var2 = new DataOutputStream(var1);
/**
* 这里写入cp的巨细,注重size()+1,可以和之前Class结构中的constant_pool_count对应
*/
var2.writeShort(this.pool.size() + 1);
Iterator var3 = this.pool.iterator();
/**
* 遍历cp中的工具,写入详细信息,对应Class结构中的cp_info
*/
while(var3.hasNext()) {
ProxyGenerator.ConstantPool.Entry var4 = (ProxyGenerator.ConstantPool.Entry)var3.next();
var4.write(var2);
}
}

接着我们回到外层方式,继续往下看

写入access_flag

data.writeShort(this.accessFlags);

写入当前类的信息

data.writeShort.writeShort(this.cp.getClass(dotToSlash(this.className)));

写入父类的信息(回忆类的属性第一条,继续了Proxy类)

data.writeShort.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));

写入接口数目

data.writeShort.writeShort(this.interfaces.length);

遍历接口,写入接口信息

Class[] interfaces = this.interfaces;
int interfaceLength = interfaces.length;
for (int i = 0; i < interfaceLength; ++i) {
    Class intf = interfaces[i];
    data.writeShort(this.cp.getClass(dotToSlash(intf.getName())));
}

写入字段数目

data.writeShort(this.fields.size());

遍历字段,写入字段信息

fieldInerator = this.fields.iterator();
while(fieldInerator.hasNext()) {
    ProxyGenerator.FieldInfo fieldInfo = (ProxyGenerator.FieldInfo) fieldInerator.next();
    fieldInfo.write(data);
}

写入方式数目

data.writeShort(this.methods.size());

遍历方式,写入方式信息

methodIterator = this.methods.iterator();
while(methodIterator.hasNext()) {
    ProxyGenerator.MethodInfo methodInfo = (ProxyGenerator.MethodInfo) methodIterator.next();
    methodInfo.write(data);
}

由于该类没有稀奇的attribute,因此attribute数目直接写0

data.writeShort(0);

正和之前的类结构完全一一对应,此时我们对proxy所做的事情就有了一个整体的掌握

 

 

领会了整体之后,下面再深入先容一下字节码中部门工具的详细花样,为后面进一步看Proxy的源码做一些准备
为了更好地明白下面的内容,我们先界说一个简朴的类Test.java

public class Test implements TestInt {
    private int field = 1;

public int add(int a, int b) {
return a + b;
}
}

interface TestInt {
}

天生.class文件

javac Test.java

查看.class文件

javap -v Test.class

获得效果

Classfile /Users/tianjiyuan/Documents/jvm/Test.class
  Last modified 2020-7-3; size 292 bytes
  MD5 checksum 1afecf9ea44088238bc8aa9804b28208
  Compiled from "Test.java"
public class Test implements TestInt
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#16         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#17         // Test.field:I
   #3 = Class              #18            // Test
   #4 = Class              #19            // java/lang/Object
   #5 = Class              #20            // TestInt
   #6 = Utf8               field
   #7 = Utf8               I
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               add
  #13 = Utf8               (II)I
  #14 = Utf8               SourceFile
  #15 = Utf8               Test.java
  #16 = NameAndType        #8:#9          // "<init>":()V
  #17 = NameAndType        #6:#7          // field:I
  #18 = Utf8               Test
  #19 = Utf8               java/lang/Object
  #20 = Utf8               TestInt
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field field:I
         9: return
      LineNumberTable:
        line 1: 0
        line 2: 4

  public int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: ireturn
      LineNumberTable:
        line 5: 0
}
SourceFile: "Test.java"

我们先看下面这3个部门正对应minor_version,major_version,access_flags

minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER

接着看Constant Pool

Constant pool:
   #1 = Methodref          #4.#16         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#17         // Test.field:I
   #3 = Class              #18            // Test
   ...
   #6 = Utf8               field
...
#16 = NameAndType #8:#9 // "<init>":()V

其中有如下几种类型

Methodref :方式的引用
Fieldref:字段的引用
Class :类的引用
Utf8 :字符串的引用
NameAndType 类型的形貌

 

下面一个一个先容

Class结构

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

示意一个类的引用
tag:示意自身的编号
name_index:必须是常量池中的有用索引,用来示意类的名字
例如

#3 = Class              #18            // Test

tag = 3,示意自身索引为3

name_index = 18,示意名字的索引是18

此时我们查看#18,即这个类的名字是Test

#18 = Utf8               Test

 

Field、Method、Interface结构

文档中这3者是放在一起的

CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

示意一个字段、方式、接口方式的引用

tag:示意自身编号
class_index:示意常量池中的一个有用索引
  如果是Methodref_info必须是Class类型的
  如果是InterfaceMethodref_info则必须是一个Interface
  如果是Fieldref_info则可以是Class或者是Interface
name_and_type_index:示意常量池中的一个有用索引(示意方式的名字、返回类型、参数)
  如果是Fieldref_info,则必须是一个对字段的形貌,否则必须是一个对方式的形貌

例如

#1 = Methodref          #4.#16         // java/lang/Object."<init>":()V

tag = 1,示意自身索引为1
class_index = 4,示意类型是索引为4的类
name_and_type_index = 16,示意方式的形貌为索引16

查看4和16

   #4 = Class              #19            // java/lang/Object
  #16 = NameAndType        #8:#9          // "<init>":()V

即示意这个方式是Object类中的组织函数

 

NameAndType结构

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

用来示意一个方式或者字段,其中不包括该字段或方式所属的类

tag:示意自身编号
name_index:常量池中的一个有用索引,必须是Utf8类型(示意方式或字段的名字)
descriptor_index:常量池中的一个有用索引,必须是Utf8类型(示意方式的返回类型和参数)

例如

#16 = NameAndType        #8:#9          // "<init>":()V

tag = 16
name_index = 8
descriptor_index = 9

查看索引8和9

   #8 = Utf8               <init>
   #9 = Utf8               ()V

方式名为<init>示意组织函数,参数0个,返回值为void

 

UTF-8结构

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

示意一个字符串常量

tag:示意自身编号
length:示意byte数组的长度
bytes[length]:示意详细数据内容
这个部门实在另有许多细节,不外这里就不展开了,有兴趣的可以自行查看jvm文档,后面会有文章详细分析

 

常量池的内容就先容到这里,接下去我们还需要看下类结构的其他成员

this_class,必须是一个有用的常量池索引,需要是CONSTANT_Class_info类型的
super_class,必须是一个有用的常量池索引,需要是CONSTANT_Class_info类型的或者为0,示意没有父类
interfaces_count,接口数目,一个int值
interfaces[],接口数组,数组中的值必须是一个常量池的有用索引,需要是CONSTANT_Class_info类型
fields_count,字段数目

fields[],字段数组,数组中的值都是field_info结构

field_info {
    u2             access_flags;//access_flag
    u2             name_index;//常量池中的一个有用索引,必须是Utf8类型(示意方式或字段的名字)
    u2             descriptor_index;//常量池中的一个有用索引,必须是Utf8类型(示意字段的形貌)
    u2             attributes_count;//跳过,本文不涉及
    attribute_info attributes[attributes_count];//跳过,本文不涉及
}

methods_count,方式数目
methods[],方式数组,结构如下

method_info {
    u2             access_flags;//access_flag
    u2             name_index;//常量池中的一个有用索引,必须是Utf8类型(示意方式或字段的名字)
    u2             descriptor_index;//常量池中的一个有用索引,必须是Utf8类型(示意方式的形貌)
    u2             attributes_count;//属性数目
    attribute_info attributes[attributes_count];//属性的详细内容
}

class文件的一些基本结构就先容到这里,下一篇文章中会继续连系Proxy的源码,进一步深入领会class的种种结构究竟是怎么被组织的

 

,

联博API接口

www.326681.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:us apple developer accounts for sale:class文件的基本结构及proxy源码剖析二

网友评论

  • (*)

最新评论

标签列表

    文章归档

      站点信息

      • 文章总数:621
      • 页面总数:0
      • 分类总数:8
      • 标签总数:995
      • 评论总数:254
      • 浏览总数:9141