开发者社区> 挚爱灬> 正文

[动态代理三部曲:中] - 从动态代理,看Class文件结构定义

简介: 前言 这篇内容是上一篇[动态代理三部曲:上] - 动态代理是如何"坑掉了"我4500块钱的补充,进一步分析篇。 建议二者结合食用,醇香绵软,入口即化。 好了,不扯淡了,开始... 正文 2、Class 文件的格式 这里为啥是2开头呢?因为上篇文章是1 这部分内容不知道各位小伙伴是怎么感觉的。
+关注继续查看

前言

这篇内容是上一篇[动态代理三部曲:上] - 动态代理是如何"坑掉了"我4500块钱的补充,进一步分析篇。
建议二者结合食用,醇香绵软,入口即化。

好了,不扯淡了,开始...

正文

2、Class 文件的格式

这里为啥是2开头呢?因为上篇文章是1

这部分内容不知道各位小伙伴是怎么感觉的。最开始学习的时候,我是一头雾水,不知道如何下手。当一步步结合反射、JVM内存模型,类加载机制后。再回过头来就会发现一起豁然开朗。

此篇内容的开始,让我们根据我们demo中所用的类:RentHouseProcessorHandler来分析这个问题。
如果我们用十六进制编辑器(比如:Sublime)打开这个RentHouseProcessorHandler.class文件:

img_4cde01fbb422ed6aba0ffc491fedb71a.png
十六进制的Class文件

说实话这一行行的文字,最开始我是拒绝的。哦,上帝,为什么要让我看这些鬼东西...其实如果我们静下心来,想当年高中时代学习数学,物理公式那样去认真的对待它。就会发现它不过就是:一堆人为的定义上了含义的符号而已。

在我们准备读懂这些十六进制文字时,先让我们看一幅《Java虚拟机规范(Java SE 7)》对class文件的定义:


img_762c573ad44c424f095b635d1c40d523.png
《Java虚拟机规范(Java SE 7)》

2.1、Class文件的规范结构

2.1.1、标准结构

上图的内容,其实非常的通俗易懂,不要因为是不常见的英文就抵触它们。让我们尝试着去翻译它们:
1、魔数;2、次版本号;3、主版本号;4、常量池数量;5、常量池;6、权限标识;7、此类;8、父类;9、接口数目;10、接口;11、变量数目;12、变量;13、方法数目;14、方法....

img_63945ca50d9c6494720b13f4808e4bfb.png
Class结构

其实是不是发现了什么,这不是就一个类应该存在的东西么?没错啊,Class文件的结构就是固定了我们编写的Class类所存放的规则而已。最开始的我,以为是深奥,没敢去了解他们。当我踌躇满志,鼓足勇气去准备好好大干一场的时候,才发现它太简单了...就是一些规则,仅此而已。

2.1.2、特别注意的结构:表

虽然只是一些规则,但规则之中,总会有一些特别需要我们去注意的地方:比如cp_info这个类型。在《深入理解Java虚拟机》中,作者把以_info结尾的类型称之为“表”。这里让我们也沿用这种表达方式。说白了,它就是拥有多级关系的类型。

cp_info 表示常量池(常量池:首先它和方法区中运行时常量池不是同一个内容。这里的常量池存放了字面量和符号引用)。


符号引用:

符号引用:

  • 类和接口的全局限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

这里符号引用的作用,我们想先一个问题。CPU执行程序的时候,实际上是去寻找对应指令的内存地址。但是我们的Class文件是先被编译出来的,但是此时还没有被JVM加载到内存,所以肯定是不可能存在内存地址这一说的。因此我们的Class文件需要一些标识,让JVM加载内容的时候从常量池中获取到对应的符号引用,然后在映射到具体的内存地址上。


放到常量池的中数据项在《Java虚拟机规范(Java SE 7)》中一共有14个常量,每一种常量都是一个“表”,并且每种常量都用一个公共的tag来表示是哪种类型的常量。具体内容如下图:


img_47d77a483a14fe05dd6b8374200d9947.png
表类型.png

这里让我们先解读一下这个常量池:让我们跳过u4的魔数、u2的此版本、u2的主版本。直接来看constant_pool_count。跳过对应的内容,那么我们的constant_pool_count就对应十六进制的20,对应十进制的32,也就是说常量池中有32个内容?实际不是的,因为设计者将第0个位置空出来另做打算。所以我们的常量池只有31个内容。


img_3e20d4bce0e34011cc793471e251edb4.png
constant_pool_count

我们可以通过javap命令证实这个问题。


img_79d62531faaf7d698b9f222a9194b20c.png
javap

接下来的一个字节:0a,翻译成十进制就是10,对应我们表中的CONSTANT_Methodref_info,而接下来的四个字节。分别代表索引3,20。这里的索引代表什么意思呢?注意理解下图中标红的地方:

img_baf07d71cea41a23deeeda6c7bc541be.png
常量池只有31个内容

接下来就不逐个解读这些内容了,因为它就是一个对应的过程。如果小伙伴们有兴趣可以自行去尝试解读一番哦。推荐一个工具JavaClassViewer,可以比较方便的查看这些内容:


img_863990a599628b57be430f63e583fba9.png
JavaClassViewer-左视图
img_4fe7ea5747d0e4686f9f91dc86073e91.png
JavaClassViewer-右视图

常量池结束之后,便是我们正常的变量,方法的信息。而这里我们需要了解一个全新的概念:描述符。
对于我们来说一个变量、方法在java源码里是什么样子我们很清楚。但是它们在class文件里是什么样子的呢?这个样子其实就被称之为:描述符。

上文谈动态代理的时候,我们了解到了ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);方法中,通过:

dout.writeInt(0xCAFEBABE);
MethodInfo minfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V",ACC_PUBLIC);    

等方法,构建了我们的$Proxy0所需要的class结构,是不是和我们javap出来的内容很类似?接下来让我们走进描述符。

2.2、变量、方法的描述符

经过上述内容的铺垫,0xCAFEBABE是什么意思,应该无需多言了。而<init>是构造方法的意思。再加上(Ljava/lang/reflect/InvocationHandler;)V以及ACC_PUBLIC就可以表示为InvocationHandler的public的构造方法,其中V表示无返回值。

  • <init>:对象构造器方法。
  • <clinit>:类构造器方法。

这里的内容就被称之为方法的描述符,让我们简单的看一些图,加深这方面内容。


img_90178d5e60663c00262cdac6130a4121.png
类型描述符

基本类型和void在描述符中都有一个大写字符和他们对应; 那么引用类型的描述符,又是什么样子的呢?

“L” + 类型的全限定名 + “;”

例如下图中的:Ljava/lang/Object;就是表示这是一个Object类型。

img_d1ec727fb53adaba1222feb72e560501.png
方法对应的描述符

而方法描述符的规则也很简单,上图中,总结出来就是一句话:

(参数类型1参数类型2参数类型3 ...)返回值类型

例如上图中的:int[] m(int i,String s)转换为描述符:(ILjava/lang/String;)[I

不知道截了这么多图,大家对描述符有没有比较明确的认识。说白了我们我们ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);中所write的内容就是具体方法的描述符。

然后通过DataOutputStream转成byte数组,那么就是我们Class文件所固定的内容了。因此,此时我们的Class文件就已经构建完毕,接下来所需要的就是将其加载到内存中,供我们使用。

2.3、收尾

到这准备结束class文件结构的内容。不知道小伙伴们是否有收获。因为篇幅是在有限,有些内容又不是一句话俩句话可以描述清楚的。所以有些内容一带而过,实在抱歉。具体细节内容,大家可以参考《深入理解Java虚拟机》。

希望大家可以谅解。

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

img_89788b3a8f3f86257453cbc8264959f6.png
个人公众号:IT面试填坑小分队

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
JavaSE:第八章:java常用类
JavaSE:第八章:java常用类
23 0
Java SE基础知识详解第[19]期—单元测试、反射、注解、动态代理
Java SE基础知识详解第[19]期—单元测试、反射、注解、动态代理
81 0
java面试题:什么是织入。什么是织入应用的不同点?
java面试题:什么是织入。什么是织入应用的不同点?
71 0
SpringBoot (走读源码)静态方法中调用spring注入的对象,注入对象为null?
SpringBoot (走读源码)静态方法中调用spring注入的对象,注入对象为null?
145 0
Spring源码学习--new()和反射的实例化区别
本文是博主学习Spring源码的记录,希望对大家有所帮助。
38 0
java核心基础--java反射机制详解
在详细了解反射机制之前,我们先来了解一下java代码在计算机中的运行过程: 比如当我们编写好一个类:Student.java,里面包含学生的姓名和年龄,构造方法,其他方法。
55 0
(十七)关于动态代理,你能说出动态代理的几种方式?
动态代理是指代理类不是写在代码中的,而是在代码运行过程中产生的,Java提供了两种方式来实现动态代理,分别是基于Jdk的动态代理和基于Cglib的动态代理。
57 0
注解式项目开发!详细分析Java中各个注解的作用和使用方式
本篇文章主要介绍了Java项目中各个注解的作用以及具体使用方式。介绍了Java的框架中每个注解的具体含义以及具体的应用场景。通过了解熟悉Java框架中各个注解的使用方式,可以使我们项目开发工作更加方便高效。
129 0
【Spring注解驱动开发】AOP核心类源码解析,这是最全的一篇了!!
昨天二狗子让我给他讲@EnableAspectJAutoProxy注解,讲到AnnotationAwareAspectJAutoProxyCreator类的源码时,二狗子消化不了了。这不,今天又来让我给他讲讲AOP的核心类。那我们就开始吧!
92 0
Spring源码分析之IOC的三种常见用法及源码实现(二)
回顾上文 我们研究的是 AnnotationConfigApplicationContext annotationConfigApplication = new AnnotationConfigApplicationContext (MainConfig.
831 0
+关注
挚爱灬
2018年毕业生,目前在人人车任职android开发工程师
文章
问答
文章排行榜
最热
最新
相关电子书
更多
JAVA反射原理以及一些常见的应用
立即下载
Java基础入门(四)-泛型、反射、注解
立即下载
低代码开发师(初级)实战教程
立即下载