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

前言

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

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

正文

2、Class 文件的格式

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

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

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

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

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


2.1、Class文件的规范结构

2.1.1、标准结构

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

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

2.1.2、特别注意的结构:表

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

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


符号引用:

符号引用:

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

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



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

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

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

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

接下来就不逐个解读这些内容了,因为它就是一个对应的过程。如果小伙伴们有兴趣可以自行去尝试解读一番哦。推荐一个工具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是什么意思,应该无需多言了。而&lt;init&gt;是构造方法的意思。再加上(Ljava/lang/reflect/InvocationHandler;)V以及ACC_PUBLIC就可以表示为InvocationHandler的public的构造方法,其中V表示无返回值。

  • &lt;init&gt;:对象构造器方法。
  • &lt;clinit&gt;:类构造器方法。

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

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

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

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

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

(参数类型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虚拟机》。

希望大家可以谅解。

原文地址:http://blog.51cto.com/13931046/2164732

时间: 2024-10-13 16:55:08

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

Java中的静态代理、通用动态代理类以及原理剖析

代理模式和静态代理 在开发中,代理模式是常用的模式之一,一般来说我们使用的代理模式基本上都是静态代理,实现模式大致如下 : 我们以网络代理为例,简单演示一下静态代理的实现 : // 网络接口 interface Network { public void surfTheInternet(); public void gotoFacebook(); } // 普通网络 class CommonNetwork implements Network { @Override public void su

23种设计模式----------代理模式(三) 之 动态代理模式

(上一篇)种设计模式----------代理模式(二) 当然代理模式中,用的最广泛的,用的最多的是  动态代理模式. 动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理. 抽象接口的类图如下: --图来自设计模式之禅 所以动态代理模式要有一个InvocationHandler接口 和 GamePlayerIH实现类.其中 InvocationHandler是JD提供的动态代理接口,对被代理类的方法进行代理. 代码实现如下 抽象主题类或者接口: 1 package com.ye

Java语言中反射动态代理接口的解释与演示

Java语言中反射动态代理接口的解释与演示 Java在JDK1.3的时候引入了动态代理机制.可以运用在框架编程与平台编程时候捕获事件.审核数据.日志等功能实现,首先看一下设计模式的UML图解: 当你调用一个接口API时候,实际实现类继承该接口,调用时候经过proxy实现. 在Java中动态代理实现的两个关键接口类与class类分别如下: java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 我们下面就通过InvocationHan

Java中的动态代理以及Proxy类的偷瞄

动态代理机制 所谓动态代理,即通过代理类Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联. Java动态代理类位于Java.lang.reflect包下,主要涉及到两个类. (1)接口InvocationHandler:该接口中仅定义了一个方法. Object invoke(Object obj, Method method, Object[] args); 在实际使用时,第一个参数obj一般指代理类,method是被代理的方法,args为该方法

Spring AOP中的动态代理

0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1 JdkDynamicAopProxy 2.2 CglibAopProxy 3 总结 0  前言 前一个季度旅游TDC的Thames服务有几次宕机,根据组内原因认真查找发现是数据库事务造成的,后来把服务中的事务配置全部去掉,服务恢复正常.根据这次教训,虽然现在还是很难确定是哪一个方面的真正原因,但是激

使用Java中的动态代理实现数据库连接池

2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的连接数据库对服务性能来讲是一个瓶颈,使用缓冲池技术可以来消除这个瓶颈.我们可以在 互联网上找到很多关于数据库连接池的源程序,但是都发现这样一个共同的问题:这些连接池的实现方法都不同程度地增加了与使用者之间的耦合度.很多的连接池 都要求用户通过其规定的方法获取数据库的连接,这一点我们可以理解,毕竟目前

浅谈代理 模式与java中的动态代理

代理模式的定义: 代理模式是一个使用律非常高的模式,定义如下: 为其他对象提供一种代理,以控制对这个对象的访问. 类图: 简单的静态代理: public interface IRunner{ //这是一个代理类和被代理类都需要实现的接口 //在接口中定义一个抽象函数 public void request(); } //下面是真实的被代理类 public class Runner implements IRunner{ //实现接口中的方法 public void request(){ //实现

java中的动态代理(二)

上一节我介绍了什么是静态代理.在静态代理中的代理对象是直接定义在代码中的,这样会导致代码不能复用并且工作量也会成倍的增加所以在日常的开发中我们更多使用的是动态代理模式.在动态代理中,代理类在是程序运行中动态生成的,在java中一般有两种方式来实现动态代理模式,它们分别是javaSDK动态代理和第三方类库cglib动态代理. 今天我介绍的是java SDK动态代理.首先我们先来看一下如何使用java SDK实现动态代理模式: public class JavaSDKProxyTest { stat

java中的动态代理机制

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的.下面通过代码来学习java中的动态代理技术. 首先定义一个接口: package com.aop.spring; /** * Created by xinfengyao on 16-2-29. */ public interface Perform { public void play(); } 实